From ccf5e1cc0ab0389f2de364669a5ade5dd50760a4 Mon Sep 17 00:00:00 2001 From: mpaulson Date: Fri, 3 Nov 2023 11:52:07 -0600 Subject: [PATCH] feat: the basic list for harpoons --- HARPOON2.md | 90 ++++++ lua/harpoon/cmd-ui.lua | 160 ---------- lua/harpoon/config.lua | 68 +++++ lua/harpoon/dev.lua | 48 --- lua/harpoon/init.lua | 287 ++---------------- lua/harpoon/list.lua | 111 +++++++ lua/harpoon/mark.lua | 420 --------------------------- lua/harpoon/term.lua | 146 ---------- lua/harpoon/test/list_spec.lua | 34 +++ lua/harpoon/test/manage-a-mark.lua | 3 - lua/harpoon/test/manage_cmd_spec.lua | 141 --------- lua/harpoon/tmux.lua | 230 --------------- lua/harpoon/ui.lua | 284 ------------------ lua/harpoon/utils.lua | 64 ---- 14 files changed, 329 insertions(+), 1757 deletions(-) create mode 100644 HARPOON2.md delete mode 100644 lua/harpoon/cmd-ui.lua create mode 100644 lua/harpoon/config.lua delete mode 100644 lua/harpoon/dev.lua create mode 100644 lua/harpoon/list.lua delete mode 100644 lua/harpoon/mark.lua delete mode 100644 lua/harpoon/term.lua create mode 100644 lua/harpoon/test/list_spec.lua delete mode 100644 lua/harpoon/test/manage-a-mark.lua delete mode 100644 lua/harpoon/test/manage_cmd_spec.lua delete mode 100644 lua/harpoon/tmux.lua delete mode 100644 lua/harpoon/ui.lua delete mode 100644 lua/harpoon/utils.lua diff --git a/HARPOON2.md b/HARPOON2.md new file mode 100644 index 0000000..b79b7ef --- /dev/null +++ b/HARPOON2.md @@ -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 + diff --git a/lua/harpoon/cmd-ui.lua b/lua/harpoon/cmd-ui.lua deleted file mode 100644 index 69caaf6..0000000 --- a/lua/harpoon/cmd-ui.lua +++ /dev/null @@ -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", - "lua require('harpoon.cmd-ui').toggle_quick_menu()", - { silent = true } - ) - vim.api.nvim_buf_set_keymap( - Harpoon_cmd_bufh, - "n", - "", - "lua require('harpoon.cmd-ui').toggle_quick_menu()", - { silent = true } - ) - vim.api.nvim_buf_set_keymap( - Harpoon_cmd_bufh, - "n", - "", - "lua require('harpoon.cmd-ui').select_menu_item()", - {} - ) - vim.cmd( - string.format( - "autocmd BufWriteCmd 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 lua require('harpoon.cmd-ui').on_menu_save()", - Harpoon_cmd_bufh - ) - ) - end - vim.cmd( - string.format( - "autocmd BufModifiedSet 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 diff --git a/lua/harpoon/config.lua b/lua/harpoon/config.lua new file mode 100644 index 0000000..01eb613 --- /dev/null +++ b/lua/harpoon/config.lua @@ -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 diff --git a/lua/harpoon/dev.lua b/lua/harpoon/dev.lua deleted file mode 100644 index b0373ee..0000000 --- a/lua/harpoon/dev.lua +++ /dev/null @@ -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 rr :lua require("harpoon.dev").reload() -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 diff --git a/lua/harpoon/init.lua b/lua/harpoon/init.lua index 88aaa3d..ed62302 100644 --- a/lua/harpoon/init.lua +++ b/lua/harpoon/init.lua @@ -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") -local data_path = vim.fn.stdpath("data") -local user_config = string.format("%s/harpoon.json", config_path) -local cache_config = string.format("%s/harpoon.json", data_path) +-- setup +-- read from a config file +-- + +---@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 the_primeagen_harpoon = - vim.api.nvim_create_augroup("THE_PRIMEAGEN_HARPOON", { clear = true }) - -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 - else - t1[k] = v - end - end +---@param c HarpoonConfig +function config(c) 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 + diff --git a/lua/harpoon/list.lua b/lua/harpoon/list.lua new file mode 100644 index 0000000..3f0cc98 --- /dev/null +++ b/lua/harpoon/list.lua @@ -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 + diff --git a/lua/harpoon/mark.lua b/lua/harpoon/mark.lua deleted file mode 100644 index 3f498d7..0000000 --- a/lua/harpoon/mark.lua +++ /dev/null @@ -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 diff --git a/lua/harpoon/term.lua b/lua/harpoon/term.lua deleted file mode 100644 index 3c429a2..0000000 --- a/lua/harpoon/term.lua +++ /dev/null @@ -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 diff --git a/lua/harpoon/test/list_spec.lua b/lua/harpoon/test/list_spec.lua new file mode 100644 index 0000000..ef6ef7b --- /dev/null +++ b/lua/harpoon/test/list_spec.lua @@ -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) + diff --git a/lua/harpoon/test/manage-a-mark.lua b/lua/harpoon/test/manage-a-mark.lua deleted file mode 100644 index df1969d..0000000 --- a/lua/harpoon/test/manage-a-mark.lua +++ /dev/null @@ -1,3 +0,0 @@ --- TODO: Harpooned --- local Marker = require('harpoon.mark') --- local eq = assert.are.same diff --git a/lua/harpoon/test/manage_cmd_spec.lua b/lua/harpoon/test/manage_cmd_spec.lua deleted file mode 100644 index f63ad51..0000000 --- a/lua/harpoon/test/manage_cmd_spec.lua +++ /dev/null @@ -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) diff --git a/lua/harpoon/tmux.lua b/lua/harpoon/tmux.lua deleted file mode 100644 index 2c04c80..0000000 --- a/lua/harpoon/tmux.lua +++ /dev/null @@ -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 diff --git a/lua/harpoon/ui.lua b/lua/harpoon/ui.lua deleted file mode 100644 index fcb310b..0000000 --- a/lua/harpoon/ui.lua +++ /dev/null @@ -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", - "lua require('harpoon.ui').toggle_quick_menu()", - { silent = true } - ) - vim.api.nvim_buf_set_keymap( - Harpoon_bufh, - "n", - "", - "lua require('harpoon.ui').toggle_quick_menu()", - { silent = true } - ) - vim.api.nvim_buf_set_keymap( - Harpoon_bufh, - "n", - "", - "lua require('harpoon.ui').select_menu_item()", - {} - ) - vim.cmd( - string.format( - "autocmd BufWriteCmd lua require('harpoon.ui').on_menu_save()", - Harpoon_bufh - ) - ) - if global_config.save_on_change then - vim.cmd( - string.format( - "autocmd TextChanged,TextChangedI lua require('harpoon.ui').on_menu_save()", - Harpoon_bufh - ) - ) - end - vim.cmd( - string.format( - "autocmd BufModifiedSet set nomodified", - Harpoon_bufh - ) - ) - vim.cmd( - "autocmd BufLeave ++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 diff --git a/lua/harpoon/utils.lua b/lua/harpoon/utils.lua deleted file mode 100644 index 0f600e6..0000000 --- a/lua/harpoon/utils.lua +++ /dev/null @@ -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