mirror of
https://github.com/ThePrimeagen/harpoon.git
synced 2025-07-14 10:00:29 +00:00
feat: the basic list for harpoons
This commit is contained in:
parent
21d0d1bfa3
commit
ccf5e1cc0a
90
HARPOON2.md
Normal file
90
HARPOON2.md
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
### Features
|
||||||
|
* select how to generate the list key
|
||||||
|
|
||||||
|
#### was
|
||||||
|
list_key = [cwd [+ git branch]]
|
||||||
|
* files
|
||||||
|
* terminals
|
||||||
|
* tmux
|
||||||
|
|
||||||
|
#### is
|
||||||
|
list_key = [key] + [list_name]
|
||||||
|
|
||||||
|
nil = default
|
||||||
|
false = turn off
|
||||||
|
|
||||||
|
listA = {
|
||||||
|
listLine({ ... })
|
||||||
|
{ ... }
|
||||||
|
{ ... }
|
||||||
|
{ ... }
|
||||||
|
}
|
||||||
|
|
||||||
|
harpoon.setup({
|
||||||
|
|
||||||
|
settings = {
|
||||||
|
jumpToFileLocation: boolean => defaults true
|
||||||
|
}
|
||||||
|
|
||||||
|
default = {
|
||||||
|
// defaults to json.parse
|
||||||
|
encode = function(obj) => string
|
||||||
|
decode = function(string) => object
|
||||||
|
key = function() ... end
|
||||||
|
display = function(listLine) => string
|
||||||
|
select = function(listLine) => void
|
||||||
|
equals = function(list_line_a, list_line_b) => boolean
|
||||||
|
|
||||||
|
# question mark: what does it take to support custom things in here?
|
||||||
|
# potentially subject to change
|
||||||
|
add = function() => void
|
||||||
|
}
|
||||||
|
|
||||||
|
frecency = {
|
||||||
|
... a file list that is generated by harpoon ...
|
||||||
|
... can be opened via viewer ...
|
||||||
|
}
|
||||||
|
|
||||||
|
events = {
|
||||||
|
on_change = function(operation, list, value)
|
||||||
|
}
|
||||||
|
|
||||||
|
project = {
|
||||||
|
//key = vim.loop.cwd
|
||||||
|
key = git origin?
|
||||||
|
}
|
||||||
|
|
||||||
|
specifics = {
|
||||||
|
key = vim.loop.cwd + git_branch
|
||||||
|
}
|
||||||
|
|
||||||
|
list_name = {
|
||||||
|
key = function() ... end
|
||||||
|
}
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
### Functionality
|
||||||
|
select by index
|
||||||
|
prev/next
|
||||||
|
addToBack
|
||||||
|
addToFront
|
||||||
|
checking for deleted files?
|
||||||
|
- perhaps this could be part of the default select operation and use error
|
||||||
|
select
|
||||||
|
- default file select should come with options so you can open split/tab as
|
||||||
|
well
|
||||||
|
|
||||||
|
harpoon.current = "default"
|
||||||
|
harpoon.current = "listName"
|
||||||
|
|
||||||
|
harpoon.set_current(list_name)
|
||||||
|
|
||||||
|
### LATER FEATUERS
|
||||||
|
frecency = later feature likely, but great idea
|
||||||
|
- https://github.com/agkozak/zsh-z
|
||||||
|
- https://en.wikipedia.org/wiki/Frecency
|
||||||
|
|
||||||
|
// i don't understand this one
|
||||||
|
harpoon -> qfix : qfix -> harpoon
|
||||||
|
|
@ -1,160 +0,0 @@
|
|||||||
local harpoon = require("harpoon")
|
|
||||||
local popup = require("plenary.popup")
|
|
||||||
local utils = require("harpoon.utils")
|
|
||||||
local log = require("harpoon.dev").log
|
|
||||||
local term = require("harpoon.term")
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
Harpoon_cmd_win_id = nil
|
|
||||||
Harpoon_cmd_bufh = nil
|
|
||||||
|
|
||||||
local function close_menu(force_save)
|
|
||||||
force_save = force_save or false
|
|
||||||
local global_config = harpoon.get_global_settings()
|
|
||||||
|
|
||||||
if global_config.save_on_toggle or force_save then
|
|
||||||
require("harpoon.cmd-ui").on_menu_save()
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_win_close(Harpoon_cmd_win_id, true)
|
|
||||||
|
|
||||||
Harpoon_cmd_win_id = nil
|
|
||||||
Harpoon_cmd_bufh = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_window()
|
|
||||||
log.trace("_create_window()")
|
|
||||||
local config = harpoon.get_menu_config()
|
|
||||||
local width = config.width or 60
|
|
||||||
local height = config.height or 10
|
|
||||||
local borderchars = config.borderchars
|
|
||||||
or { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }
|
|
||||||
local bufnr = vim.api.nvim_create_buf(false, false)
|
|
||||||
|
|
||||||
local Harpoon_cmd_win_id, win = popup.create(bufnr, {
|
|
||||||
title = "Harpoon Commands",
|
|
||||||
highlight = "HarpoonWindow",
|
|
||||||
line = math.floor(((vim.o.lines - height) / 2) - 1),
|
|
||||||
col = math.floor((vim.o.columns - width) / 2),
|
|
||||||
minwidth = width,
|
|
||||||
minheight = height,
|
|
||||||
borderchars = borderchars,
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.api.nvim_win_set_option(
|
|
||||||
win.border.win_id,
|
|
||||||
"winhl",
|
|
||||||
"Normal:HarpoonBorder"
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
bufnr = bufnr,
|
|
||||||
win_id = Harpoon_cmd_win_id,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_menu_items()
|
|
||||||
log.trace("_get_menu_items()")
|
|
||||||
local lines = vim.api.nvim_buf_get_lines(Harpoon_cmd_bufh, 0, -1, true)
|
|
||||||
local indices = {}
|
|
||||||
|
|
||||||
for _, line in pairs(lines) do
|
|
||||||
if not utils.is_white_space(line) then
|
|
||||||
table.insert(indices, line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return indices
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.toggle_quick_menu()
|
|
||||||
log.trace("cmd-ui#toggle_quick_menu()")
|
|
||||||
if
|
|
||||||
Harpoon_cmd_win_id ~= nil
|
|
||||||
and vim.api.nvim_win_is_valid(Harpoon_cmd_win_id)
|
|
||||||
then
|
|
||||||
close_menu()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local win_info = create_window()
|
|
||||||
local contents = {}
|
|
||||||
local global_config = harpoon.get_global_settings()
|
|
||||||
|
|
||||||
Harpoon_cmd_win_id = win_info.win_id
|
|
||||||
Harpoon_cmd_bufh = win_info.bufnr
|
|
||||||
|
|
||||||
for idx, cmd in pairs(harpoon.get_term_config().cmds) do
|
|
||||||
contents[idx] = cmd
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_win_set_option(Harpoon_cmd_win_id, "number", true)
|
|
||||||
vim.api.nvim_buf_set_name(Harpoon_cmd_bufh, "harpoon-cmd-menu")
|
|
||||||
vim.api.nvim_buf_set_lines(Harpoon_cmd_bufh, 0, #contents, false, contents)
|
|
||||||
vim.api.nvim_buf_set_option(Harpoon_cmd_bufh, "filetype", "harpoon")
|
|
||||||
vim.api.nvim_buf_set_option(Harpoon_cmd_bufh, "buftype", "acwrite")
|
|
||||||
vim.api.nvim_buf_set_option(Harpoon_cmd_bufh, "bufhidden", "delete")
|
|
||||||
vim.api.nvim_buf_set_keymap(
|
|
||||||
Harpoon_cmd_bufh,
|
|
||||||
"n",
|
|
||||||
"q",
|
|
||||||
"<Cmd>lua require('harpoon.cmd-ui').toggle_quick_menu()<CR>",
|
|
||||||
{ silent = true }
|
|
||||||
)
|
|
||||||
vim.api.nvim_buf_set_keymap(
|
|
||||||
Harpoon_cmd_bufh,
|
|
||||||
"n",
|
|
||||||
"<ESC>",
|
|
||||||
"<Cmd>lua require('harpoon.cmd-ui').toggle_quick_menu()<CR>",
|
|
||||||
{ silent = true }
|
|
||||||
)
|
|
||||||
vim.api.nvim_buf_set_keymap(
|
|
||||||
Harpoon_cmd_bufh,
|
|
||||||
"n",
|
|
||||||
"<CR>",
|
|
||||||
"<Cmd>lua require('harpoon.cmd-ui').select_menu_item()<CR>",
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
vim.cmd(
|
|
||||||
string.format(
|
|
||||||
"autocmd BufWriteCmd <buffer=%s> lua require('harpoon.cmd-ui').on_menu_save()",
|
|
||||||
Harpoon_cmd_bufh
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if global_config.save_on_change then
|
|
||||||
vim.cmd(
|
|
||||||
string.format(
|
|
||||||
"autocmd TextChanged,TextChangedI <buffer=%s> lua require('harpoon.cmd-ui').on_menu_save()",
|
|
||||||
Harpoon_cmd_bufh
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
vim.cmd(
|
|
||||||
string.format(
|
|
||||||
"autocmd BufModifiedSet <buffer=%s> set nomodified",
|
|
||||||
Harpoon_cmd_bufh
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.select_menu_item()
|
|
||||||
log.trace("cmd-ui#select_menu_item()")
|
|
||||||
local cmd = vim.fn.line(".")
|
|
||||||
close_menu(true)
|
|
||||||
local answer = vim.fn.input("Terminal index (default to 1): ")
|
|
||||||
if answer == "" then
|
|
||||||
answer = "1"
|
|
||||||
end
|
|
||||||
local idx = tonumber(answer)
|
|
||||||
if idx then
|
|
||||||
term.sendCommand(idx, cmd)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.on_menu_save()
|
|
||||||
log.trace("cmd-ui#on_menu_save()")
|
|
||||||
term.set_cmd_list(get_menu_items())
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
68
lua/harpoon/config.lua
Normal file
68
lua/harpoon/config.lua
Normal file
@ -0,0 +1,68 @@
|
|||||||
|
local M = {}
|
||||||
|
|
||||||
|
function M.get_config(config, name)
|
||||||
|
return vim.tbl_extend("force", {}, config.default, config[name] or {})
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.get_default_config()
|
||||||
|
return {
|
||||||
|
settings = {
|
||||||
|
save_on_toggle = true,
|
||||||
|
jump_to_file_location = true,
|
||||||
|
},
|
||||||
|
|
||||||
|
default = {
|
||||||
|
---@param obj HarpoonListItem
|
||||||
|
---@return string
|
||||||
|
encode = function(obj)
|
||||||
|
return vim.json.encode(obj)
|
||||||
|
end,
|
||||||
|
|
||||||
|
---@param str string
|
||||||
|
---@return HarpoonListItem
|
||||||
|
decode = function(str)
|
||||||
|
return vim.json.decode(str)
|
||||||
|
end,
|
||||||
|
|
||||||
|
key = function()
|
||||||
|
return vim.loop.cwd()
|
||||||
|
end,
|
||||||
|
|
||||||
|
---@param list_item HarpoonListItem
|
||||||
|
display = function(list_item)
|
||||||
|
return list_item.value
|
||||||
|
end,
|
||||||
|
|
||||||
|
---@param list_item HarpoonListItem
|
||||||
|
select = function(list_item)
|
||||||
|
error("please implement select")
|
||||||
|
end,
|
||||||
|
|
||||||
|
---@param list_item_a HarpoonListItem
|
||||||
|
---@param list_item_b HarpoonListItem
|
||||||
|
equals = function(list_item_a, list_item_b)
|
||||||
|
return list_item_a.value == list_item_b.value
|
||||||
|
end,
|
||||||
|
|
||||||
|
add = function()
|
||||||
|
error("please implement add")
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
function M.merge_config(partial_config, latest_config)
|
||||||
|
local config = latest_config or M.get_default_config()
|
||||||
|
for k, v in pairs(partial_config) do
|
||||||
|
if k == "settings" then
|
||||||
|
config.settings = vim.tbl_extend("force", config.settings, v)
|
||||||
|
elseif k == "default" then
|
||||||
|
config.default = vim.tbl_extend("force", config.default, v)
|
||||||
|
else
|
||||||
|
config[k] = vim.tbl_extend("force", config[k] or {}, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return config
|
||||||
|
end
|
||||||
|
|
||||||
|
return M
|
@ -1,48 +0,0 @@
|
|||||||
-- Don't include this file, we should manually include it via
|
|
||||||
-- require("harpoon.dev").reload();
|
|
||||||
--
|
|
||||||
-- A quick mapping can be setup using something like:
|
|
||||||
-- :nmap <leader>rr :lua require("harpoon.dev").reload()<CR>
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
function M.reload()
|
|
||||||
require("plenary.reload").reload_module("harpoon")
|
|
||||||
end
|
|
||||||
|
|
||||||
local log_levels = { "trace", "debug", "info", "warn", "error", "fatal" }
|
|
||||||
local function set_log_level()
|
|
||||||
local log_level = vim.env.HARPOON_LOG or vim.g.harpoon_log_level
|
|
||||||
|
|
||||||
for _, level in pairs(log_levels) do
|
|
||||||
if level == log_level then
|
|
||||||
return log_level
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return "warn" -- default, if user hasn't set to one from log_levels
|
|
||||||
end
|
|
||||||
|
|
||||||
local log_level = set_log_level()
|
|
||||||
M.log = require("plenary.log").new({
|
|
||||||
plugin = "harpoon",
|
|
||||||
level = log_level,
|
|
||||||
})
|
|
||||||
|
|
||||||
local log_key = os.time()
|
|
||||||
|
|
||||||
local function override(key)
|
|
||||||
local fn = M.log[key]
|
|
||||||
M.log[key] = function(...)
|
|
||||||
fn(log_key, ...)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
for _, v in pairs(log_levels) do
|
|
||||||
override(v)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_log_key()
|
|
||||||
return log_key
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
@ -1,268 +1,33 @@
|
|||||||
local Path = require("plenary.path")
|
|
||||||
local utils = require("harpoon.utils")
|
|
||||||
local Dev = require("harpoon.dev")
|
|
||||||
local log = Dev.log
|
|
||||||
|
|
||||||
local config_path = vim.fn.stdpath("config")
|
-- setup
|
||||||
local data_path = vim.fn.stdpath("data")
|
-- read from a config file
|
||||||
local user_config = string.format("%s/harpoon.json", config_path)
|
--
|
||||||
local cache_config = string.format("%s/harpoon.json", data_path)
|
|
||||||
|
---@alias HarpoonListItem {value: any, context: any}
|
||||||
|
|
||||||
|
---@class HarpoonPartialConfigItem
|
||||||
|
---@field encode? (fun(list_item: HarpoonListItem): string)
|
||||||
|
---@field decode? (fun(obj: string): any)
|
||||||
|
---@field key? (fun(): string)
|
||||||
|
---@field display? (fun(list_item: HarpoonListItem): string)
|
||||||
|
---@field select? (fun(list_item: HarpoonListItem): nil)
|
||||||
|
---@field equals? (fun(list_line_a: HarpoonListItem, list_line_b: HarpoonListItem): boolean)
|
||||||
|
---@field add? fun(): HarpoonListItem
|
||||||
|
|
||||||
|
---@class HarpoonSettings
|
||||||
|
---@field save_on_toggle boolean defaults to true
|
||||||
|
---@field jump_to_file_location boolean defaults to true
|
||||||
|
|
||||||
|
---@class HarpoonConfig
|
||||||
|
---@field default HarpoonPartialConfigItem
|
||||||
|
---@field settings HarpoonSettings
|
||||||
|
---@field [string] HarpoonPartialConfigItem
|
||||||
|
|
||||||
local M = {}
|
local M = {}
|
||||||
|
|
||||||
local the_primeagen_harpoon =
|
---@param c HarpoonConfig
|
||||||
vim.api.nvim_create_augroup("THE_PRIMEAGEN_HARPOON", { clear = true })
|
function config(c)
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd({ "BufLeave, VimLeave" }, {
|
|
||||||
callback = function()
|
|
||||||
require("harpoon.mark").store_offset()
|
|
||||||
end,
|
|
||||||
group = the_primeagen_harpoon,
|
|
||||||
})
|
|
||||||
|
|
||||||
--[[
|
|
||||||
{
|
|
||||||
projects = {
|
|
||||||
["/path/to/director"] = {
|
|
||||||
term = {
|
|
||||||
cmds = {
|
|
||||||
}
|
|
||||||
... is there anything that could be options?
|
|
||||||
},
|
|
||||||
mark = {
|
|
||||||
marks = {
|
|
||||||
}
|
|
||||||
... is there anything that could be options?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
... high level settings
|
|
||||||
}
|
|
||||||
--]]
|
|
||||||
HarpoonConfig = HarpoonConfig or {}
|
|
||||||
|
|
||||||
-- tbl_deep_extend does not work the way you would think
|
|
||||||
local function merge_table_impl(t1, t2)
|
|
||||||
for k, v in pairs(t2) do
|
|
||||||
if type(v) == "table" then
|
|
||||||
if type(t1[k]) == "table" then
|
|
||||||
merge_table_impl(t1[k], v)
|
|
||||||
else
|
|
||||||
t1[k] = v
|
|
||||||
end
|
end
|
||||||
else
|
|
||||||
t1[k] = v
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function mark_config_key(global_settings)
|
|
||||||
global_settings = global_settings or M.get_global_settings()
|
|
||||||
if global_settings.mark_branch then
|
|
||||||
return utils.branch_key()
|
|
||||||
else
|
|
||||||
return utils.project_key()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function merge_tables(...)
|
|
||||||
log.trace("_merge_tables()")
|
|
||||||
local out = {}
|
|
||||||
for i = 1, select("#", ...) do
|
|
||||||
merge_table_impl(out, select(i, ...))
|
|
||||||
end
|
|
||||||
return out
|
|
||||||
end
|
|
||||||
|
|
||||||
local function ensure_correct_config(config)
|
|
||||||
log.trace("_ensure_correct_config()")
|
|
||||||
local projects = config.projects
|
|
||||||
local mark_key = mark_config_key(config.global_settings)
|
|
||||||
if projects[mark_key] == nil then
|
|
||||||
log.debug("ensure_correct_config(): No config found for:", mark_key)
|
|
||||||
projects[mark_key] = {
|
|
||||||
mark = { marks = {} },
|
|
||||||
term = {
|
|
||||||
cmds = {},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local proj = projects[mark_key]
|
|
||||||
if proj.mark == nil then
|
|
||||||
log.debug("ensure_correct_config(): No marks found for", mark_key)
|
|
||||||
proj.mark = { marks = {} }
|
|
||||||
end
|
|
||||||
|
|
||||||
if proj.term == nil then
|
|
||||||
log.debug(
|
|
||||||
"ensure_correct_config(): No terminal commands found for",
|
|
||||||
mark_key
|
|
||||||
)
|
|
||||||
proj.term = { cmds = {} }
|
|
||||||
end
|
|
||||||
|
|
||||||
local marks = proj.mark.marks
|
|
||||||
|
|
||||||
for idx, mark in pairs(marks) do
|
|
||||||
if type(mark) == "string" then
|
|
||||||
mark = { filename = mark }
|
|
||||||
marks[idx] = mark
|
|
||||||
end
|
|
||||||
|
|
||||||
marks[idx].filename = utils.normalize_path(mark.filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
return config
|
|
||||||
end
|
|
||||||
|
|
||||||
local function expand_dir(config)
|
|
||||||
log.trace("_expand_dir(): Config pre-expansion:", config)
|
|
||||||
|
|
||||||
local projects = config.projects or {}
|
|
||||||
for k in pairs(projects) do
|
|
||||||
local expanded_path = Path.new(k):expand()
|
|
||||||
projects[expanded_path] = projects[k]
|
|
||||||
if expanded_path ~= k then
|
|
||||||
projects[k] = nil
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
log.trace("_expand_dir(): Config post-expansion:", config)
|
|
||||||
return config
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.save()
|
|
||||||
-- first refresh from disk everything but our project
|
|
||||||
M.refresh_projects_b4update()
|
|
||||||
|
|
||||||
log.trace("save(): Saving cache config to", cache_config)
|
|
||||||
Path:new(cache_config):write(vim.fn.json_encode(HarpoonConfig), "w")
|
|
||||||
end
|
|
||||||
|
|
||||||
local function read_config(local_config)
|
|
||||||
log.trace("_read_config():", local_config)
|
|
||||||
return vim.json.decode(Path:new(local_config):read())
|
|
||||||
end
|
|
||||||
|
|
||||||
-- 1. saved. Where do we save?
|
|
||||||
function M.setup(config)
|
|
||||||
log.trace("setup(): Setting up...")
|
|
||||||
|
|
||||||
if not config then
|
|
||||||
config = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok, u_config = pcall(read_config, user_config)
|
|
||||||
|
|
||||||
if not ok then
|
|
||||||
log.debug("setup(): No user config present at", user_config)
|
|
||||||
u_config = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local ok2, c_config = pcall(read_config, cache_config)
|
|
||||||
|
|
||||||
if not ok2 then
|
|
||||||
log.debug("setup(): No cache config present at", cache_config)
|
|
||||||
c_config = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
local complete_config = merge_tables({
|
|
||||||
projects = {},
|
|
||||||
global_settings = {
|
|
||||||
["save_on_toggle"] = false,
|
|
||||||
["save_on_change"] = true,
|
|
||||||
["enter_on_sendcmd"] = false,
|
|
||||||
["tmux_autoclose_windows"] = false,
|
|
||||||
["excluded_filetypes"] = { "harpoon" },
|
|
||||||
["mark_branch"] = false,
|
|
||||||
},
|
|
||||||
}, expand_dir(c_config), expand_dir(u_config), expand_dir(config))
|
|
||||||
|
|
||||||
-- There was this issue where the vim.loop.cwd() didn't have marks or term, but had
|
|
||||||
-- an object for vim.loop.cwd()
|
|
||||||
ensure_correct_config(complete_config)
|
|
||||||
|
|
||||||
HarpoonConfig = complete_config
|
|
||||||
log.debug("setup(): Complete config", HarpoonConfig)
|
|
||||||
log.trace("setup(): log_key", Dev.get_log_key())
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_global_settings()
|
|
||||||
log.trace("get_global_settings()")
|
|
||||||
return HarpoonConfig.global_settings
|
|
||||||
end
|
|
||||||
|
|
||||||
-- refresh all projects from disk, except our current one
|
|
||||||
function M.refresh_projects_b4update()
|
|
||||||
log.trace(
|
|
||||||
"refresh_projects_b4update(): refreshing other projects",
|
|
||||||
cache_config
|
|
||||||
)
|
|
||||||
-- save current runtime version of our project config for merging back in later
|
|
||||||
local cwd = mark_config_key()
|
|
||||||
local current_p_config = {
|
|
||||||
projects = {
|
|
||||||
[cwd] = ensure_correct_config(HarpoonConfig).projects[cwd],
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
-- erase all projects from global config, will be loaded back from disk
|
|
||||||
HarpoonConfig.projects = nil
|
|
||||||
|
|
||||||
-- this reads a stale version of our project but up-to-date versions
|
|
||||||
-- of all other projects
|
|
||||||
local ok2, c_config = pcall(read_config, cache_config)
|
|
||||||
|
|
||||||
if not ok2 then
|
|
||||||
log.debug(
|
|
||||||
"refresh_projects_b4update(): No cache config present at",
|
|
||||||
cache_config
|
|
||||||
)
|
|
||||||
c_config = { projects = {} }
|
|
||||||
end
|
|
||||||
-- don't override non-project config in HarpoonConfig later
|
|
||||||
c_config = { projects = c_config.projects }
|
|
||||||
|
|
||||||
-- erase our own project, will be merged in from current_p_config later
|
|
||||||
c_config.projects[cwd] = nil
|
|
||||||
|
|
||||||
local complete_config = merge_tables(
|
|
||||||
HarpoonConfig,
|
|
||||||
expand_dir(c_config),
|
|
||||||
expand_dir(current_p_config)
|
|
||||||
)
|
|
||||||
|
|
||||||
-- There was this issue where the vim.loop.cwd() didn't have marks or term, but had
|
|
||||||
-- an object for vim.loop.cwd()
|
|
||||||
ensure_correct_config(complete_config)
|
|
||||||
|
|
||||||
HarpoonConfig = complete_config
|
|
||||||
log.debug("refresh_projects_b4update(): Complete config", HarpoonConfig)
|
|
||||||
log.trace("refresh_projects_b4update(): log_key", Dev.get_log_key())
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_term_config()
|
|
||||||
log.trace("get_term_config()")
|
|
||||||
return ensure_correct_config(HarpoonConfig).projects[utils.project_key()].term
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_mark_config()
|
|
||||||
log.trace("get_mark_config()")
|
|
||||||
return ensure_correct_config(HarpoonConfig).projects[mark_config_key()].mark
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_menu_config()
|
|
||||||
log.trace("get_menu_config()")
|
|
||||||
return HarpoonConfig.menu or {}
|
|
||||||
end
|
|
||||||
|
|
||||||
-- should only be called for debug purposes
|
|
||||||
function M.print_config()
|
|
||||||
print(vim.inspect(HarpoonConfig))
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Sets a default config with no values
|
|
||||||
M.setup()
|
|
||||||
|
|
||||||
return M
|
return M
|
||||||
|
|
||||||
|
111
lua/harpoon/list.lua
Normal file
111
lua/harpoon/list.lua
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
local get_config = require "harpoon.config".get_config
|
||||||
|
|
||||||
|
-- TODO: Define the config object
|
||||||
|
|
||||||
|
--- @class Item
|
||||||
|
--- @field value string
|
||||||
|
--- @field context any
|
||||||
|
|
||||||
|
--- create a table object to be new'd
|
||||||
|
--- @class List
|
||||||
|
--- @field config any
|
||||||
|
--- @field name string
|
||||||
|
--- @field items Item[]
|
||||||
|
local List = {}
|
||||||
|
|
||||||
|
List.__index = List
|
||||||
|
function List:new(config, name, items)
|
||||||
|
return setmetatable({
|
||||||
|
items = items,
|
||||||
|
config = config,
|
||||||
|
name = name,
|
||||||
|
}, self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function List:push(item)
|
||||||
|
table.insert(self.items, item)
|
||||||
|
end
|
||||||
|
|
||||||
|
function List:addToFront(item)
|
||||||
|
table.insert(self.items, 1, item)
|
||||||
|
end
|
||||||
|
|
||||||
|
function List:remove(item)
|
||||||
|
for i, v in ipairs(self.items) do
|
||||||
|
if get_config(self.config, self.name)(v, item) then
|
||||||
|
table.remove(self.items, i)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function List:removeAt(index)
|
||||||
|
table.remove(self.items, index)
|
||||||
|
end
|
||||||
|
|
||||||
|
function List:get(index)
|
||||||
|
return self.items[index]
|
||||||
|
end
|
||||||
|
|
||||||
|
--- much inefficiencies. dun care
|
||||||
|
---@param displayed string[]
|
||||||
|
function List:resolve_displayed(displayed)
|
||||||
|
local not_found = {}
|
||||||
|
local config = get_config(self.config, self.name)
|
||||||
|
for _, v in ipairs(displayed) do
|
||||||
|
local found = false
|
||||||
|
for _, in_table in ipairs(self.items) do
|
||||||
|
found = config.display(in_table, v)
|
||||||
|
break
|
||||||
|
end
|
||||||
|
|
||||||
|
if not found then
|
||||||
|
table.insert(not_found, v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
for _, v in ipairs(not_found) do
|
||||||
|
self:remove(v)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @return string[]
|
||||||
|
function List:display()
|
||||||
|
local out = {}
|
||||||
|
local config = get_config(self.config, self.name)
|
||||||
|
for _, v in ipairs(self.items) do
|
||||||
|
table.insert(out, config.display(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @return string[]
|
||||||
|
function List:encode()
|
||||||
|
local out = {}
|
||||||
|
local config = get_config(self.config, self.name)
|
||||||
|
for _, v in ipairs(self.items) do
|
||||||
|
table.insert(out, config.encode(v))
|
||||||
|
end
|
||||||
|
|
||||||
|
return out
|
||||||
|
end
|
||||||
|
|
||||||
|
--- @return List
|
||||||
|
--- @param config HarpoonConfig
|
||||||
|
--- @param name string
|
||||||
|
--- @param items string[]
|
||||||
|
function List.decode(config, name, items)
|
||||||
|
local list_items = {}
|
||||||
|
local c = get_config(config, name)
|
||||||
|
|
||||||
|
for _, item in ipairs(items) do
|
||||||
|
table.insert(list_items, c.decode(item))
|
||||||
|
end
|
||||||
|
|
||||||
|
return List:new(config, name, list_items)
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
return List
|
||||||
|
|
@ -1,420 +0,0 @@
|
|||||||
local harpoon = require("harpoon")
|
|
||||||
local utils = require("harpoon.utils")
|
|
||||||
local log = require("harpoon.dev").log
|
|
||||||
|
|
||||||
-- I think that I may have to organize this better. I am not the biggest fan
|
|
||||||
-- of procedural all the things
|
|
||||||
local M = {}
|
|
||||||
local callbacks = {}
|
|
||||||
|
|
||||||
-- I am trying to avoid over engineering the whole thing. We will likely only
|
|
||||||
-- need one event emitted
|
|
||||||
local function emit_changed()
|
|
||||||
log.trace("_emit_changed()")
|
|
||||||
if harpoon.get_global_settings().save_on_change then
|
|
||||||
harpoon.save()
|
|
||||||
end
|
|
||||||
|
|
||||||
if not callbacks["changed"] then
|
|
||||||
log.trace("_emit_changed(): no callbacks for 'changed', returning")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
for idx, cb in pairs(callbacks["changed"]) do
|
|
||||||
log.trace(
|
|
||||||
string.format(
|
|
||||||
"_emit_changed(): Running callback #%d for 'changed'",
|
|
||||||
idx
|
|
||||||
)
|
|
||||||
)
|
|
||||||
cb()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function filter_empty_string(list)
|
|
||||||
log.trace("_filter_empty_string()")
|
|
||||||
local next = {}
|
|
||||||
for idx = 1, #list do
|
|
||||||
if list[idx] ~= "" then
|
|
||||||
table.insert(next, list[idx].filename)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return next
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_first_empty_slot()
|
|
||||||
log.trace("_get_first_empty_slot()")
|
|
||||||
for idx = 1, M.get_length() do
|
|
||||||
local filename = M.get_marked_file_name(idx)
|
|
||||||
if filename == "" then
|
|
||||||
return idx
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return M.get_length() + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_buf_name(id)
|
|
||||||
log.trace("_get_buf_name():", id)
|
|
||||||
if id == nil then
|
|
||||||
return utils.normalize_path(vim.api.nvim_buf_get_name(0))
|
|
||||||
elseif type(id) == "string" then
|
|
||||||
return utils.normalize_path(id)
|
|
||||||
end
|
|
||||||
|
|
||||||
local idx = M.get_index_of(id)
|
|
||||||
if M.valid_index(idx) then
|
|
||||||
return M.get_marked_file_name(idx)
|
|
||||||
end
|
|
||||||
--
|
|
||||||
-- not sure what to do here...
|
|
||||||
--
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_mark(filename)
|
|
||||||
local cursor_pos = vim.api.nvim_win_get_cursor(0)
|
|
||||||
log.trace(
|
|
||||||
string.format(
|
|
||||||
"_create_mark(): Creating mark at row: %d, col: %d for %s",
|
|
||||||
cursor_pos[1],
|
|
||||||
cursor_pos[2],
|
|
||||||
filename
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return {
|
|
||||||
filename = filename,
|
|
||||||
row = cursor_pos[1],
|
|
||||||
col = cursor_pos[2],
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function mark_exists(buf_name)
|
|
||||||
log.trace("_mark_exists()")
|
|
||||||
for idx = 1, M.get_length() do
|
|
||||||
if M.get_marked_file_name(idx) == buf_name then
|
|
||||||
log.debug("_mark_exists(): Mark exists", buf_name)
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
log.debug("_mark_exists(): Mark doesn't exist", buf_name)
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local function validate_buf_name(buf_name)
|
|
||||||
log.trace("_validate_buf_name():", buf_name)
|
|
||||||
if buf_name == "" or buf_name == nil then
|
|
||||||
log.error(
|
|
||||||
"_validate_buf_name(): Not a valid name for a mark,",
|
|
||||||
buf_name
|
|
||||||
)
|
|
||||||
error("Couldn't find a valid file name to mark, sorry.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
local function filter_filetype()
|
|
||||||
local current_filetype = vim.bo.filetype
|
|
||||||
local excluded_filetypes = harpoon.get_global_settings().excluded_filetypes
|
|
||||||
|
|
||||||
if current_filetype == "harpoon" then
|
|
||||||
log.error("filter_filetype(): You can't add harpoon to the harpoon")
|
|
||||||
error("You can't add harpoon to the harpoon")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if vim.tbl_contains(excluded_filetypes, current_filetype) then
|
|
||||||
log.error(
|
|
||||||
'filter_filetype(): This filetype cannot be added or is included in the "excluded_filetypes" option'
|
|
||||||
)
|
|
||||||
error(
|
|
||||||
'This filetype cannot be added or is included in the "excluded_filetypes" option'
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_index_of(item, marks)
|
|
||||||
log.trace("get_index_of():", item)
|
|
||||||
if item == nil then
|
|
||||||
log.error(
|
|
||||||
"get_index_of(): Function has been supplied with a nil value."
|
|
||||||
)
|
|
||||||
error(
|
|
||||||
"You have provided a nil value to Harpoon, please provide a string rep of the file or the file idx."
|
|
||||||
)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(item) == "string" then
|
|
||||||
local relative_item = utils.normalize_path(item)
|
|
||||||
if marks == nil then
|
|
||||||
marks = harpoon.get_mark_config().marks
|
|
||||||
end
|
|
||||||
for idx = 1, M.get_length(marks) do
|
|
||||||
if marks[idx] and marks[idx].filename == relative_item then
|
|
||||||
return idx
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- TODO move this to a "harpoon_" prefix or global config?
|
|
||||||
if vim.g.manage_a_mark_zero_index then
|
|
||||||
item = item + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if item <= M.get_length() and item >= 1 then
|
|
||||||
return item
|
|
||||||
end
|
|
||||||
|
|
||||||
log.debug("get_index_of(): No item found,", item)
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.status(bufnr)
|
|
||||||
log.trace("status()")
|
|
||||||
local buf_name
|
|
||||||
if bufnr then
|
|
||||||
buf_name = vim.api.nvim_buf_get_name(bufnr)
|
|
||||||
else
|
|
||||||
buf_name = vim.api.nvim_buf_get_name(0)
|
|
||||||
end
|
|
||||||
|
|
||||||
local norm_name = utils.normalize_path(buf_name)
|
|
||||||
local idx = M.get_index_of(norm_name)
|
|
||||||
|
|
||||||
if M.valid_index(idx) then
|
|
||||||
return "M" .. idx
|
|
||||||
end
|
|
||||||
return ""
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.valid_index(idx, marks)
|
|
||||||
log.trace("valid_index():", idx)
|
|
||||||
if idx == nil then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
|
|
||||||
local file_name = M.get_marked_file_name(idx, marks)
|
|
||||||
return file_name ~= nil and file_name ~= ""
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.add_file(file_name_or_buf_id)
|
|
||||||
filter_filetype()
|
|
||||||
local buf_name = get_buf_name(file_name_or_buf_id)
|
|
||||||
log.trace("add_file():", buf_name)
|
|
||||||
|
|
||||||
if M.valid_index(M.get_index_of(buf_name)) then
|
|
||||||
-- we don't alter file layout.
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
validate_buf_name(buf_name)
|
|
||||||
|
|
||||||
local found_idx = get_first_empty_slot()
|
|
||||||
harpoon.get_mark_config().marks[found_idx] = create_mark(buf_name)
|
|
||||||
M.remove_empty_tail(false)
|
|
||||||
emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
-- _emit_on_changed == false should only be used internally
|
|
||||||
function M.remove_empty_tail(_emit_on_changed)
|
|
||||||
log.trace("remove_empty_tail()")
|
|
||||||
_emit_on_changed = _emit_on_changed == nil or _emit_on_changed
|
|
||||||
local config = harpoon.get_mark_config()
|
|
||||||
local found = false
|
|
||||||
|
|
||||||
for i = M.get_length(), 1, -1 do
|
|
||||||
local filename = M.get_marked_file_name(i)
|
|
||||||
if filename ~= "" then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
if filename == "" then
|
|
||||||
table.remove(config.marks, i)
|
|
||||||
found = found or _emit_on_changed
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
if found then
|
|
||||||
emit_changed()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.store_offset()
|
|
||||||
log.trace("store_offset()")
|
|
||||||
local ok, res = pcall(function()
|
|
||||||
local marks = harpoon.get_mark_config().marks
|
|
||||||
local buf_name = get_buf_name()
|
|
||||||
local idx = M.get_index_of(buf_name, marks)
|
|
||||||
if not M.valid_index(idx, marks) then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local cursor_pos = vim.api.nvim_win_get_cursor(0)
|
|
||||||
log.debug(
|
|
||||||
string.format(
|
|
||||||
"store_offset(): Stored row: %d, col: %d",
|
|
||||||
cursor_pos[1],
|
|
||||||
cursor_pos[2]
|
|
||||||
)
|
|
||||||
)
|
|
||||||
marks[idx].row = cursor_pos[1]
|
|
||||||
marks[idx].col = cursor_pos[2]
|
|
||||||
end)
|
|
||||||
|
|
||||||
if not ok then
|
|
||||||
log.warn("store_offset(): Could not store offset:", res)
|
|
||||||
end
|
|
||||||
|
|
||||||
emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.rm_file(file_name_or_buf_id)
|
|
||||||
local buf_name = get_buf_name(file_name_or_buf_id)
|
|
||||||
local idx = M.get_index_of(buf_name)
|
|
||||||
log.trace("rm_file(): Removing mark at id", idx)
|
|
||||||
|
|
||||||
if not M.valid_index(idx) then
|
|
||||||
log.debug("rm_file(): No mark exists for id", file_name_or_buf_id)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
harpoon.get_mark_config().marks[idx] = create_mark("")
|
|
||||||
M.remove_empty_tail(false)
|
|
||||||
emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.clear_all()
|
|
||||||
harpoon.get_mark_config().marks = {}
|
|
||||||
log.trace("clear_all(): Clearing all marks.")
|
|
||||||
emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
--- ENTERPRISE PROGRAMMING
|
|
||||||
function M.get_marked_file(idxOrName)
|
|
||||||
log.trace("get_marked_file():", idxOrName)
|
|
||||||
if type(idxOrName) == "string" then
|
|
||||||
idxOrName = M.get_index_of(idxOrName)
|
|
||||||
end
|
|
||||||
return harpoon.get_mark_config().marks[idxOrName]
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_marked_file_name(idx, marks)
|
|
||||||
local mark
|
|
||||||
if marks ~= nil then
|
|
||||||
mark = marks[idx]
|
|
||||||
else
|
|
||||||
mark = harpoon.get_mark_config().marks[idx]
|
|
||||||
end
|
|
||||||
log.trace("get_marked_file_name():", mark and mark.filename)
|
|
||||||
return mark and mark.filename
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_length(marks)
|
|
||||||
if marks == nil then
|
|
||||||
marks = harpoon.get_mark_config().marks
|
|
||||||
end
|
|
||||||
log.trace("get_length()")
|
|
||||||
return table.maxn(marks)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.set_current_at(idx)
|
|
||||||
filter_filetype()
|
|
||||||
local buf_name = get_buf_name()
|
|
||||||
log.trace("set_current_at(): Setting id", idx, buf_name)
|
|
||||||
local config = harpoon.get_mark_config()
|
|
||||||
local current_idx = M.get_index_of(buf_name)
|
|
||||||
|
|
||||||
-- Remove it if it already exists
|
|
||||||
if M.valid_index(current_idx) then
|
|
||||||
config.marks[current_idx] = create_mark("")
|
|
||||||
end
|
|
||||||
|
|
||||||
config.marks[idx] = create_mark(buf_name)
|
|
||||||
|
|
||||||
for i = 1, M.get_length() do
|
|
||||||
if not config.marks[i] then
|
|
||||||
config.marks[i] = create_mark("")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.to_quickfix_list()
|
|
||||||
log.trace("to_quickfix_list(): Sending marks to quickfix list.")
|
|
||||||
local config = harpoon.get_mark_config()
|
|
||||||
local file_list = filter_empty_string(config.marks)
|
|
||||||
local qf_list = {}
|
|
||||||
for idx = 1, #file_list do
|
|
||||||
local mark = M.get_marked_file(idx)
|
|
||||||
qf_list[idx] = {
|
|
||||||
text = string.format("%d: %s", idx, file_list[idx]),
|
|
||||||
filename = mark.filename,
|
|
||||||
row = mark.row,
|
|
||||||
col = mark.col,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
log.debug("to_quickfix_list(): qf_list:", qf_list)
|
|
||||||
vim.fn.setqflist(qf_list)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.set_mark_list(new_list)
|
|
||||||
log.trace("set_mark_list(): New list:", new_list)
|
|
||||||
|
|
||||||
local config = harpoon.get_mark_config()
|
|
||||||
|
|
||||||
for k, v in pairs(new_list) do
|
|
||||||
if type(v) == "string" then
|
|
||||||
local mark = M.get_marked_file(v)
|
|
||||||
if not mark then
|
|
||||||
mark = create_mark(v)
|
|
||||||
end
|
|
||||||
|
|
||||||
new_list[k] = mark
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
config.marks = new_list
|
|
||||||
emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.toggle_file(file_name_or_buf_id)
|
|
||||||
local buf_name = get_buf_name(file_name_or_buf_id)
|
|
||||||
log.trace("toggle_file():", buf_name)
|
|
||||||
|
|
||||||
validate_buf_name(buf_name)
|
|
||||||
|
|
||||||
if mark_exists(buf_name) then
|
|
||||||
M.rm_file(buf_name)
|
|
||||||
print("Mark removed")
|
|
||||||
log.debug("toggle_file(): Mark removed")
|
|
||||||
else
|
|
||||||
M.add_file(buf_name)
|
|
||||||
print("Mark added")
|
|
||||||
log.debug("toggle_file(): Mark added")
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_current_index()
|
|
||||||
log.trace("get_current_index()")
|
|
||||||
return M.get_index_of(vim.api.nvim_buf_get_name(0))
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.on(event, cb)
|
|
||||||
log.trace("on():", event)
|
|
||||||
if not callbacks[event] then
|
|
||||||
log.debug("on(): no callbacks yet for", event)
|
|
||||||
callbacks[event] = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
table.insert(callbacks[event], cb)
|
|
||||||
log.debug("on(): All callbacks:", callbacks)
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
@ -1,146 +0,0 @@
|
|||||||
local harpoon = require("harpoon")
|
|
||||||
local log = require("harpoon.dev").log
|
|
||||||
local global_config = harpoon.get_global_settings()
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
local terminals = {}
|
|
||||||
|
|
||||||
local function create_terminal(create_with)
|
|
||||||
if not create_with then
|
|
||||||
create_with = ":terminal"
|
|
||||||
end
|
|
||||||
log.trace("term: _create_terminal(): Init:", create_with)
|
|
||||||
local current_id = vim.api.nvim_get_current_buf()
|
|
||||||
|
|
||||||
vim.cmd(create_with)
|
|
||||||
local buf_id = vim.api.nvim_get_current_buf()
|
|
||||||
local term_id = vim.b.terminal_job_id
|
|
||||||
|
|
||||||
if term_id == nil then
|
|
||||||
log.error("_create_terminal(): term_id is nil")
|
|
||||||
-- TODO: Throw an error?
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Make sure the term buffer has "hidden" set so it doesn't get thrown
|
|
||||||
-- away and cause an error
|
|
||||||
vim.api.nvim_buf_set_option(buf_id, "bufhidden", "hide")
|
|
||||||
|
|
||||||
-- Resets the buffer back to the old one
|
|
||||||
vim.api.nvim_set_current_buf(current_id)
|
|
||||||
return buf_id, term_id
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_terminal(args)
|
|
||||||
log.trace("term: _find_terminal(): Terminal:", args)
|
|
||||||
if type(args) == "number" then
|
|
||||||
args = { idx = args }
|
|
||||||
end
|
|
||||||
local term_handle = terminals[args.idx]
|
|
||||||
if not term_handle or not vim.api.nvim_buf_is_valid(term_handle.buf_id) then
|
|
||||||
local buf_id, term_id = create_terminal(args.create_with)
|
|
||||||
if buf_id == nil then
|
|
||||||
error("Failed to find and create terminal.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
term_handle = {
|
|
||||||
buf_id = buf_id,
|
|
||||||
term_id = term_id,
|
|
||||||
}
|
|
||||||
terminals[args.idx] = term_handle
|
|
||||||
end
|
|
||||||
return term_handle
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_first_empty_slot()
|
|
||||||
log.trace("_get_first_empty_slot()")
|
|
||||||
for idx, cmd in pairs(harpoon.get_term_config().cmds) do
|
|
||||||
if cmd == "" then
|
|
||||||
return idx
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return M.get_length() + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.gotoTerminal(idx)
|
|
||||||
log.trace("term: gotoTerminal(): Terminal:", idx)
|
|
||||||
local term_handle = find_terminal(idx)
|
|
||||||
|
|
||||||
vim.api.nvim_set_current_buf(term_handle.buf_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.sendCommand(idx, cmd, ...)
|
|
||||||
log.trace("term: sendCommand(): Terminal:", idx)
|
|
||||||
local term_handle = find_terminal(idx)
|
|
||||||
|
|
||||||
if type(cmd) == "number" then
|
|
||||||
cmd = harpoon.get_term_config().cmds[cmd]
|
|
||||||
end
|
|
||||||
|
|
||||||
if global_config.enter_on_sendcmd then
|
|
||||||
cmd = cmd .. "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
if cmd then
|
|
||||||
log.debug("sendCommand:", cmd)
|
|
||||||
vim.api.nvim_chan_send(term_handle.term_id, string.format(cmd, ...))
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.clear_all()
|
|
||||||
log.trace("term: clear_all(): Clearing all terminals.")
|
|
||||||
for _, term in ipairs(terminals) do
|
|
||||||
vim.api.nvim_buf_delete(term.buf_id, { force = true })
|
|
||||||
end
|
|
||||||
terminals = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_length()
|
|
||||||
log.trace("_get_length()")
|
|
||||||
return table.maxn(harpoon.get_term_config().cmds)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.valid_index(idx)
|
|
||||||
if idx == nil or idx > M.get_length() or idx <= 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.emit_changed()
|
|
||||||
log.trace("_emit_changed()")
|
|
||||||
if harpoon.get_global_settings().save_on_change then
|
|
||||||
harpoon.save()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.add_cmd(cmd)
|
|
||||||
log.trace("add_cmd()")
|
|
||||||
local found_idx = get_first_empty_slot()
|
|
||||||
harpoon.get_term_config().cmds[found_idx] = cmd
|
|
||||||
M.emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.rm_cmd(idx)
|
|
||||||
log.trace("rm_cmd()")
|
|
||||||
if not M.valid_index(idx) then
|
|
||||||
log.debug("rm_cmd(): no cmd exists for index", idx)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
table.remove(harpoon.get_term_config().cmds, idx)
|
|
||||||
M.emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.set_cmd_list(new_list)
|
|
||||||
log.trace("set_cmd_list(): New list:", new_list)
|
|
||||||
for k in pairs(harpoon.get_term_config().cmds) do
|
|
||||||
harpoon.get_term_config().cmds[k] = nil
|
|
||||||
end
|
|
||||||
for k, v in pairs(new_list) do
|
|
||||||
harpoon.get_term_config().cmds[k] = v
|
|
||||||
end
|
|
||||||
M.emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
34
lua/harpoon/test/list_spec.lua
Normal file
34
lua/harpoon/test/list_spec.lua
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
local List = require("harpoon.list")
|
||||||
|
local Config = require("harpoon.config")
|
||||||
|
local eq = assert.are.same
|
||||||
|
|
||||||
|
describe("list", function()
|
||||||
|
it("decode", function()
|
||||||
|
|
||||||
|
local config = Config.merge_config({
|
||||||
|
foo = {
|
||||||
|
decode = function(item)
|
||||||
|
-- split item on :
|
||||||
|
local parts = vim.split(item, ":")
|
||||||
|
return {
|
||||||
|
value = parts,
|
||||||
|
context = nil,
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
|
||||||
|
display = function(item)
|
||||||
|
return table.concat(item.value, "---")
|
||||||
|
end
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
local list = List.decode(config, "foo", {"foo:bar", "baz:qux"})
|
||||||
|
local displayed = list:display()
|
||||||
|
|
||||||
|
eq(displayed, {
|
||||||
|
"foo---bar",
|
||||||
|
"baz---qux",
|
||||||
|
})
|
||||||
|
end)
|
||||||
|
end)
|
||||||
|
|
@ -1,3 +0,0 @@
|
|||||||
-- TODO: Harpooned
|
|
||||||
-- local Marker = require('harpoon.mark')
|
|
||||||
-- local eq = assert.are.same
|
|
@ -1,141 +0,0 @@
|
|||||||
local harpoon = require("harpoon")
|
|
||||||
local term = require("harpoon.term")
|
|
||||||
|
|
||||||
local function assert_table_equals(tbl1, tbl2)
|
|
||||||
if #tbl1 ~= #tbl2 then
|
|
||||||
assert(false, "" .. #tbl1 .. " != " .. #tbl2)
|
|
||||||
end
|
|
||||||
for i = 1, #tbl1 do
|
|
||||||
if tbl1[i] ~= tbl2[i] then
|
|
||||||
assert.equals(tbl1[i], tbl2[i])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe("basic functionalities", function()
|
|
||||||
local emitted
|
|
||||||
local cmds
|
|
||||||
|
|
||||||
before_each(function()
|
|
||||||
emitted = false
|
|
||||||
cmds = {}
|
|
||||||
harpoon.get_term_config = function()
|
|
||||||
return {
|
|
||||||
cmds = cmds,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
term.emit_changed = function()
|
|
||||||
emitted = true
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("add_cmd for empty", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
local expected_result = {
|
|
||||||
"cmake ..",
|
|
||||||
}
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
assert.equals(emitted, true)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("add_cmd for non_empty", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
local expected_result = {
|
|
||||||
"cmake ..",
|
|
||||||
"make",
|
|
||||||
"ninja",
|
|
||||||
}
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
assert.equals(emitted, true)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("rm_cmd: removing a valid element", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
term.rm_cmd(2)
|
|
||||||
local expected_result = {
|
|
||||||
"cmake ..",
|
|
||||||
"ninja",
|
|
||||||
}
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
assert.equals(emitted, true)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("rm_cmd: remove first element", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
term.rm_cmd(1)
|
|
||||||
local expected_result = {
|
|
||||||
"make",
|
|
||||||
"ninja",
|
|
||||||
}
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
assert.equals(emitted, true)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("rm_cmd: remove last element", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
term.rm_cmd(3)
|
|
||||||
local expected_result = {
|
|
||||||
"cmake ..",
|
|
||||||
"make",
|
|
||||||
}
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
assert.equals(emitted, true)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("rm_cmd: trying to remove invalid element", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
term.rm_cmd(5)
|
|
||||||
local expected_result = {
|
|
||||||
"cmake ..",
|
|
||||||
"make",
|
|
||||||
"ninja",
|
|
||||||
}
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
assert.equals(emitted, true)
|
|
||||||
term.rm_cmd(0)
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
term.rm_cmd(-1)
|
|
||||||
assert_table_equals(harpoon.get_term_config().cmds, expected_result)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("get_length", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
assert.equals(term.get_length(), 3)
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("valid_index", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
assert(term.valid_index(1))
|
|
||||||
assert(term.valid_index(2))
|
|
||||||
assert(term.valid_index(3))
|
|
||||||
assert(not term.valid_index(0))
|
|
||||||
assert(not term.valid_index(-1))
|
|
||||||
assert(not term.valid_index(4))
|
|
||||||
end)
|
|
||||||
|
|
||||||
it("set_cmd_list", function()
|
|
||||||
term.add_cmd("cmake ..")
|
|
||||||
term.add_cmd("make")
|
|
||||||
term.add_cmd("ninja")
|
|
||||||
term.set_cmd_list({ "make uninstall", "make install" })
|
|
||||||
local expected_result = {
|
|
||||||
"make uninstall",
|
|
||||||
"make install",
|
|
||||||
}
|
|
||||||
assert_table_equals(expected_result, harpoon.get_term_config().cmds)
|
|
||||||
end)
|
|
||||||
end)
|
|
@ -1,230 +0,0 @@
|
|||||||
local harpoon = require("harpoon")
|
|
||||||
local log = require("harpoon.dev").log
|
|
||||||
local global_config = harpoon.get_global_settings()
|
|
||||||
local utils = require("harpoon.utils")
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
local tmux_windows = {}
|
|
||||||
|
|
||||||
if global_config.tmux_autoclose_windows then
|
|
||||||
local harpoon_tmux_group =
|
|
||||||
vim.api.nvim_create_augroup("HARPOON_TMUX", { clear = true })
|
|
||||||
|
|
||||||
vim.api.nvim_create_autocmd("VimLeave", {
|
|
||||||
callback = function()
|
|
||||||
require("harpoon.tmux").clear_all()
|
|
||||||
end,
|
|
||||||
group = harpoon_tmux_group,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_terminal()
|
|
||||||
log.trace("tmux: _create_terminal())")
|
|
||||||
|
|
||||||
local window_id
|
|
||||||
|
|
||||||
-- Create a new tmux window and store the window id
|
|
||||||
local out, ret, _ = utils.get_os_command_output({
|
|
||||||
"tmux",
|
|
||||||
"new-window",
|
|
||||||
"-P",
|
|
||||||
"-F",
|
|
||||||
"#{pane_id}",
|
|
||||||
}, vim.loop.cwd())
|
|
||||||
|
|
||||||
if ret == 0 then
|
|
||||||
window_id = out[1]:sub(2)
|
|
||||||
end
|
|
||||||
|
|
||||||
if window_id == nil then
|
|
||||||
log.error("tmux: _create_terminal(): window_id is nil")
|
|
||||||
return nil
|
|
||||||
end
|
|
||||||
|
|
||||||
return window_id
|
|
||||||
end
|
|
||||||
|
|
||||||
-- Checks if the tmux window with the given window id exists
|
|
||||||
local function terminal_exists(window_id)
|
|
||||||
log.trace("_terminal_exists(): Window:", window_id)
|
|
||||||
|
|
||||||
local exists = false
|
|
||||||
|
|
||||||
local window_list, _, _ = utils.get_os_command_output({
|
|
||||||
"tmux",
|
|
||||||
"list-windows",
|
|
||||||
}, vim.loop.cwd())
|
|
||||||
|
|
||||||
-- This has to be done this way because tmux has-session does not give
|
|
||||||
-- updated results
|
|
||||||
for _, line in pairs(window_list) do
|
|
||||||
local window_info = utils.split_string(line, "@")[2]
|
|
||||||
|
|
||||||
if string.find(window_info, string.sub(window_id, 2)) then
|
|
||||||
exists = true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return exists
|
|
||||||
end
|
|
||||||
|
|
||||||
local function find_terminal(args)
|
|
||||||
log.trace("tmux: _find_terminal(): Window:", args)
|
|
||||||
|
|
||||||
if type(args) == "string" then
|
|
||||||
-- assume args is a valid tmux target identifier
|
|
||||||
-- if invalid, the error returned by tmux will be thrown
|
|
||||||
return {
|
|
||||||
window_id = args,
|
|
||||||
pane = true,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
if type(args) == "number" then
|
|
||||||
args = { idx = args }
|
|
||||||
end
|
|
||||||
|
|
||||||
local window_handle = tmux_windows[args.idx]
|
|
||||||
local window_exists
|
|
||||||
|
|
||||||
if window_handle then
|
|
||||||
window_exists = terminal_exists(window_handle.window_id)
|
|
||||||
end
|
|
||||||
|
|
||||||
if not window_handle or not window_exists then
|
|
||||||
local window_id = create_terminal()
|
|
||||||
|
|
||||||
if window_id == nil then
|
|
||||||
error("Failed to find and create tmux window.")
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
window_handle = {
|
|
||||||
window_id = "%" .. window_id,
|
|
||||||
}
|
|
||||||
|
|
||||||
tmux_windows[args.idx] = window_handle
|
|
||||||
end
|
|
||||||
|
|
||||||
return window_handle
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_first_empty_slot()
|
|
||||||
log.trace("_get_first_empty_slot()")
|
|
||||||
for idx, cmd in pairs(harpoon.get_term_config().cmds) do
|
|
||||||
if cmd == "" then
|
|
||||||
return idx
|
|
||||||
end
|
|
||||||
end
|
|
||||||
return M.get_length() + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.gotoTerminal(idx)
|
|
||||||
log.trace("tmux: gotoTerminal(): Window:", idx)
|
|
||||||
local window_handle = find_terminal(idx)
|
|
||||||
|
|
||||||
local _, ret, stderr = utils.get_os_command_output({
|
|
||||||
"tmux",
|
|
||||||
window_handle.pane and "select-pane" or "select-window",
|
|
||||||
"-t",
|
|
||||||
window_handle.window_id,
|
|
||||||
}, vim.loop.cwd())
|
|
||||||
|
|
||||||
if ret ~= 0 then
|
|
||||||
error("Failed to go to terminal." .. stderr[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.sendCommand(idx, cmd, ...)
|
|
||||||
log.trace("tmux: sendCommand(): Window:", idx)
|
|
||||||
local window_handle = find_terminal(idx)
|
|
||||||
|
|
||||||
if type(cmd) == "number" then
|
|
||||||
cmd = harpoon.get_term_config().cmds[cmd]
|
|
||||||
end
|
|
||||||
|
|
||||||
if global_config.enter_on_sendcmd then
|
|
||||||
cmd = cmd .. "\n"
|
|
||||||
end
|
|
||||||
|
|
||||||
if cmd then
|
|
||||||
log.debug("sendCommand:", cmd)
|
|
||||||
|
|
||||||
local _, ret, stderr = utils.get_os_command_output({
|
|
||||||
"tmux",
|
|
||||||
"send-keys",
|
|
||||||
"-t",
|
|
||||||
window_handle.window_id,
|
|
||||||
string.format(cmd, ...),
|
|
||||||
}, vim.loop.cwd())
|
|
||||||
|
|
||||||
if ret ~= 0 then
|
|
||||||
error("Failed to send command. " .. stderr[1])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.clear_all()
|
|
||||||
log.trace("tmux: clear_all(): Clearing all tmux windows.")
|
|
||||||
|
|
||||||
for _, window in pairs(tmux_windows) do
|
|
||||||
-- Delete the current tmux window
|
|
||||||
utils.get_os_command_output({
|
|
||||||
"tmux",
|
|
||||||
"kill-window",
|
|
||||||
"-t",
|
|
||||||
window.window_id,
|
|
||||||
}, vim.loop.cwd())
|
|
||||||
end
|
|
||||||
|
|
||||||
tmux_windows = {}
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_length()
|
|
||||||
log.trace("_get_length()")
|
|
||||||
return table.maxn(harpoon.get_term_config().cmds)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.valid_index(idx)
|
|
||||||
if idx == nil or idx > M.get_length() or idx <= 0 then
|
|
||||||
return false
|
|
||||||
end
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.emit_changed()
|
|
||||||
log.trace("_emit_changed()")
|
|
||||||
if harpoon.get_global_settings().save_on_change then
|
|
||||||
harpoon.save()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.add_cmd(cmd)
|
|
||||||
log.trace("add_cmd()")
|
|
||||||
local found_idx = get_first_empty_slot()
|
|
||||||
harpoon.get_term_config().cmds[found_idx] = cmd
|
|
||||||
M.emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.rm_cmd(idx)
|
|
||||||
log.trace("rm_cmd()")
|
|
||||||
if not M.valid_index(idx) then
|
|
||||||
log.debug("rm_cmd(): no cmd exists for index", idx)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
table.remove(harpoon.get_term_config().cmds, idx)
|
|
||||||
M.emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.set_cmd_list(new_list)
|
|
||||||
log.trace("set_cmd_list(): New list:", new_list)
|
|
||||||
for k in pairs(harpoon.get_term_config().cmds) do
|
|
||||||
harpoon.get_term_config().cmds[k] = nil
|
|
||||||
end
|
|
||||||
for k, v in pairs(new_list) do
|
|
||||||
harpoon.get_term_config().cmds[k] = v
|
|
||||||
end
|
|
||||||
M.emit_changed()
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
@ -1,284 +0,0 @@
|
|||||||
local harpoon = require("harpoon")
|
|
||||||
local popup = require("plenary.popup")
|
|
||||||
local Marked = require("harpoon.mark")
|
|
||||||
local utils = require("harpoon.utils")
|
|
||||||
local log = require("harpoon.dev").log
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
Harpoon_win_id = nil
|
|
||||||
Harpoon_bufh = nil
|
|
||||||
|
|
||||||
-- We save before we close because we use the state of the buffer as the list
|
|
||||||
-- of items.
|
|
||||||
local function close_menu(force_save)
|
|
||||||
force_save = force_save or false
|
|
||||||
local global_config = harpoon.get_global_settings()
|
|
||||||
|
|
||||||
if global_config.save_on_toggle or force_save then
|
|
||||||
require("harpoon.ui").on_menu_save()
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_win_close(Harpoon_win_id, true)
|
|
||||||
|
|
||||||
Harpoon_win_id = nil
|
|
||||||
Harpoon_bufh = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
local function create_window()
|
|
||||||
log.trace("_create_window()")
|
|
||||||
local config = harpoon.get_menu_config()
|
|
||||||
local width = config.width or 60
|
|
||||||
local height = config.height or 10
|
|
||||||
local borderchars = config.borderchars
|
|
||||||
or { "─", "│", "─", "│", "╭", "╮", "╯", "╰" }
|
|
||||||
local bufnr = vim.api.nvim_create_buf(false, false)
|
|
||||||
|
|
||||||
local Harpoon_win_id, win = popup.create(bufnr, {
|
|
||||||
title = "Harpoon",
|
|
||||||
highlight = "HarpoonWindow",
|
|
||||||
line = math.floor(((vim.o.lines - height) / 2) - 1),
|
|
||||||
col = math.floor((vim.o.columns - width) / 2),
|
|
||||||
minwidth = width,
|
|
||||||
minheight = height,
|
|
||||||
borderchars = borderchars,
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.api.nvim_win_set_option(
|
|
||||||
win.border.win_id,
|
|
||||||
"winhl",
|
|
||||||
"Normal:HarpoonBorder"
|
|
||||||
)
|
|
||||||
|
|
||||||
return {
|
|
||||||
bufnr = bufnr,
|
|
||||||
win_id = Harpoon_win_id,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_menu_items()
|
|
||||||
log.trace("_get_menu_items()")
|
|
||||||
local lines = vim.api.nvim_buf_get_lines(Harpoon_bufh, 0, -1, true)
|
|
||||||
local indices = {}
|
|
||||||
|
|
||||||
for _, line in pairs(lines) do
|
|
||||||
if not utils.is_white_space(line) then
|
|
||||||
table.insert(indices, line)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
return indices
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.toggle_quick_menu()
|
|
||||||
log.trace("toggle_quick_menu()")
|
|
||||||
if Harpoon_win_id ~= nil and vim.api.nvim_win_is_valid(Harpoon_win_id) then
|
|
||||||
close_menu()
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local win_info = create_window()
|
|
||||||
local contents = {}
|
|
||||||
local global_config = harpoon.get_global_settings()
|
|
||||||
|
|
||||||
Harpoon_win_id = win_info.win_id
|
|
||||||
Harpoon_bufh = win_info.bufnr
|
|
||||||
|
|
||||||
for idx = 1, Marked.get_length() do
|
|
||||||
local file = Marked.get_marked_file_name(idx)
|
|
||||||
if file == "" then
|
|
||||||
file = "(empty)"
|
|
||||||
end
|
|
||||||
contents[idx] = string.format("%s", file)
|
|
||||||
end
|
|
||||||
|
|
||||||
vim.api.nvim_win_set_option(Harpoon_win_id, "number", true)
|
|
||||||
vim.api.nvim_buf_set_name(Harpoon_bufh, "harpoon-menu")
|
|
||||||
vim.api.nvim_buf_set_lines(Harpoon_bufh, 0, #contents, false, contents)
|
|
||||||
vim.api.nvim_buf_set_option(Harpoon_bufh, "filetype", "harpoon")
|
|
||||||
vim.api.nvim_buf_set_option(Harpoon_bufh, "buftype", "acwrite")
|
|
||||||
vim.api.nvim_buf_set_option(Harpoon_bufh, "bufhidden", "delete")
|
|
||||||
vim.api.nvim_buf_set_keymap(
|
|
||||||
Harpoon_bufh,
|
|
||||||
"n",
|
|
||||||
"q",
|
|
||||||
"<Cmd>lua require('harpoon.ui').toggle_quick_menu()<CR>",
|
|
||||||
{ silent = true }
|
|
||||||
)
|
|
||||||
vim.api.nvim_buf_set_keymap(
|
|
||||||
Harpoon_bufh,
|
|
||||||
"n",
|
|
||||||
"<ESC>",
|
|
||||||
"<Cmd>lua require('harpoon.ui').toggle_quick_menu()<CR>",
|
|
||||||
{ silent = true }
|
|
||||||
)
|
|
||||||
vim.api.nvim_buf_set_keymap(
|
|
||||||
Harpoon_bufh,
|
|
||||||
"n",
|
|
||||||
"<CR>",
|
|
||||||
"<Cmd>lua require('harpoon.ui').select_menu_item()<CR>",
|
|
||||||
{}
|
|
||||||
)
|
|
||||||
vim.cmd(
|
|
||||||
string.format(
|
|
||||||
"autocmd BufWriteCmd <buffer=%s> lua require('harpoon.ui').on_menu_save()",
|
|
||||||
Harpoon_bufh
|
|
||||||
)
|
|
||||||
)
|
|
||||||
if global_config.save_on_change then
|
|
||||||
vim.cmd(
|
|
||||||
string.format(
|
|
||||||
"autocmd TextChanged,TextChangedI <buffer=%s> lua require('harpoon.ui').on_menu_save()",
|
|
||||||
Harpoon_bufh
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
vim.cmd(
|
|
||||||
string.format(
|
|
||||||
"autocmd BufModifiedSet <buffer=%s> set nomodified",
|
|
||||||
Harpoon_bufh
|
|
||||||
)
|
|
||||||
)
|
|
||||||
vim.cmd(
|
|
||||||
"autocmd BufLeave <buffer> ++nested ++once silent lua require('harpoon.ui').toggle_quick_menu()"
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.select_menu_item()
|
|
||||||
local idx = vim.fn.line(".")
|
|
||||||
close_menu(true)
|
|
||||||
M.nav_file(idx)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.on_menu_save()
|
|
||||||
log.trace("on_menu_save()")
|
|
||||||
Marked.set_mark_list(get_menu_items())
|
|
||||||
end
|
|
||||||
|
|
||||||
local function get_or_create_buffer(filename)
|
|
||||||
local buf_exists = vim.fn.bufexists(filename) ~= 0
|
|
||||||
if buf_exists then
|
|
||||||
return vim.fn.bufnr(filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
return vim.fn.bufadd(filename)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.nav_file(id)
|
|
||||||
log.trace("nav_file(): Navigating to", id)
|
|
||||||
local idx = Marked.get_index_of(id)
|
|
||||||
if not Marked.valid_index(idx) then
|
|
||||||
log.debug("nav_file(): No mark exists for id", id)
|
|
||||||
return
|
|
||||||
end
|
|
||||||
|
|
||||||
local mark = Marked.get_marked_file(idx)
|
|
||||||
local filename = mark.filename
|
|
||||||
if filename:sub(1, 1) ~= "/" then
|
|
||||||
filename = vim.loop.cwd() .. "/" .. mark.filename
|
|
||||||
end
|
|
||||||
local buf_id = get_or_create_buffer(filename)
|
|
||||||
local set_row = not vim.api.nvim_buf_is_loaded(buf_id)
|
|
||||||
|
|
||||||
vim.api.nvim_set_current_buf(buf_id)
|
|
||||||
vim.api.nvim_buf_set_option(buf_id, "buflisted", true)
|
|
||||||
if set_row and mark.row and mark.col then
|
|
||||||
vim.cmd(string.format(":call cursor(%d, %d)", mark.row, mark.col))
|
|
||||||
log.debug(
|
|
||||||
string.format(
|
|
||||||
"nav_file(): Setting cursor to row: %d, col: %d",
|
|
||||||
mark.row,
|
|
||||||
mark.col
|
|
||||||
)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.location_window(options)
|
|
||||||
local default_options = {
|
|
||||||
relative = "editor",
|
|
||||||
style = "minimal",
|
|
||||||
width = 30,
|
|
||||||
height = 15,
|
|
||||||
row = 2,
|
|
||||||
col = 2,
|
|
||||||
}
|
|
||||||
options = vim.tbl_extend("keep", options, default_options)
|
|
||||||
|
|
||||||
local bufnr = options.bufnr or vim.api.nvim_create_buf(false, true)
|
|
||||||
local win_id = vim.api.nvim_open_win(bufnr, true, options)
|
|
||||||
|
|
||||||
return {
|
|
||||||
bufnr = bufnr,
|
|
||||||
win_id = win_id,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.notification(text)
|
|
||||||
local win_stats = vim.api.nvim_list_uis()[1]
|
|
||||||
local win_width = win_stats.width
|
|
||||||
|
|
||||||
local prev_win = vim.api.nvim_get_current_win()
|
|
||||||
|
|
||||||
local info = M.location_window({
|
|
||||||
width = 20,
|
|
||||||
height = 2,
|
|
||||||
row = 1,
|
|
||||||
col = win_width - 21,
|
|
||||||
})
|
|
||||||
|
|
||||||
vim.api.nvim_buf_set_lines(
|
|
||||||
info.bufnr,
|
|
||||||
0,
|
|
||||||
5,
|
|
||||||
false,
|
|
||||||
{ "!!! Notification", text }
|
|
||||||
)
|
|
||||||
vim.api.nvim_set_current_win(prev_win)
|
|
||||||
|
|
||||||
return {
|
|
||||||
bufnr = info.bufnr,
|
|
||||||
win_id = info.win_id,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.close_notification(bufnr)
|
|
||||||
vim.api.nvim_buf_delete(bufnr)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.nav_next()
|
|
||||||
log.trace("nav_next()")
|
|
||||||
local current_index = Marked.get_current_index()
|
|
||||||
local number_of_items = Marked.get_length()
|
|
||||||
|
|
||||||
if current_index == nil then
|
|
||||||
current_index = 1
|
|
||||||
else
|
|
||||||
current_index = current_index + 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if current_index > number_of_items then
|
|
||||||
current_index = 1
|
|
||||||
end
|
|
||||||
M.nav_file(current_index)
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.nav_prev()
|
|
||||||
log.trace("nav_prev()")
|
|
||||||
local current_index = Marked.get_current_index()
|
|
||||||
local number_of_items = Marked.get_length()
|
|
||||||
|
|
||||||
if current_index == nil then
|
|
||||||
current_index = number_of_items
|
|
||||||
else
|
|
||||||
current_index = current_index - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if current_index < 1 then
|
|
||||||
current_index = number_of_items
|
|
||||||
end
|
|
||||||
|
|
||||||
M.nav_file(current_index)
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
@ -1,64 +0,0 @@
|
|||||||
local Path = require("plenary.path")
|
|
||||||
local data_path = vim.fn.stdpath("data")
|
|
||||||
local Job = require("plenary.job")
|
|
||||||
|
|
||||||
local M = {}
|
|
||||||
|
|
||||||
M.data_path = data_path
|
|
||||||
|
|
||||||
function M.project_key()
|
|
||||||
return vim.loop.cwd()
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.branch_key()
|
|
||||||
-- `git branch --show-current` requires Git v2.22.0+ so going with more
|
|
||||||
-- widely available command
|
|
||||||
local branch = M.get_os_command_output({
|
|
||||||
"git",
|
|
||||||
"rev-parse",
|
|
||||||
"--abbrev-ref",
|
|
||||||
"HEAD",
|
|
||||||
})[1]
|
|
||||||
|
|
||||||
if branch then
|
|
||||||
return vim.loop.cwd() .. "-" .. branch
|
|
||||||
else
|
|
||||||
return M.project_key()
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.normalize_path(item)
|
|
||||||
return Path:new(item):make_relative(M.project_key())
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.get_os_command_output(cmd, cwd)
|
|
||||||
if type(cmd) ~= "table" then
|
|
||||||
print("Harpoon: [get_os_command_output]: cmd has to be a table")
|
|
||||||
return {}
|
|
||||||
end
|
|
||||||
local command = table.remove(cmd, 1)
|
|
||||||
local stderr = {}
|
|
||||||
local stdout, ret = Job:new({
|
|
||||||
command = command,
|
|
||||||
args = cmd,
|
|
||||||
cwd = cwd,
|
|
||||||
on_stderr = function(_, data)
|
|
||||||
table.insert(stderr, data)
|
|
||||||
end,
|
|
||||||
}):sync()
|
|
||||||
return stdout, ret, stderr
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.split_string(str, delimiter)
|
|
||||||
local result = {}
|
|
||||||
for match in (str .. delimiter):gmatch("(.-)" .. delimiter) do
|
|
||||||
table.insert(result, match)
|
|
||||||
end
|
|
||||||
return result
|
|
||||||
end
|
|
||||||
|
|
||||||
function M.is_white_space(str)
|
|
||||||
return str:gsub("%s", "") == ""
|
|
||||||
end
|
|
||||||
|
|
||||||
return M
|
|
Loading…
x
Reference in New Issue
Block a user