diff --git a/README.md b/README.md index e4f0d0f..369725f 100644 --- a/README.md +++ b/README.md @@ -122,6 +122,8 @@ Note, these examples assume you are using the [lazy.nvim](https://github.com/fol keys = { { "m", "Grapple toggle", desc = "Grapple toggle tag" }, { "M", "Grapple toggle_tags", desc = "Grapple open tags window" }, + { "n", "Grapple cycle_tags next", desc = "Grapple cycle next tag" }, + { "p", "Grapple cycle_tags prev", desc = "Grapple cycle previous tag" }, }, }, ``` @@ -150,8 +152,8 @@ Example configuration similar to [harpoon.nvim](https://github.com/ThePrimeagen/ { "", "Grapple select index=3", desc = "Select third tag" }, { "", "Grapple select index=4", desc = "Select fourth tag" }, - { "", "Grapple cycle backward", desc = "Go to previous tag" }, - { "", "Grapple cycle forward", desc = "Go to next tag" }, + { "", "Grapple cycle_tags next", desc = "Go to next tag" }, + { "", "Grapple cycle_tags prev", desc = "Go to previous tag" }, }, }, ``` @@ -178,8 +180,8 @@ Example configuration similar to [arrow.nvim](https://github.com/otavioschwanck/ { ";", "Grapple toggle_tags", desc = "Toggle tags menu" }, { "", "Grapple toggle", desc = "Toggle tag" }, - { "H", "Grapple cycle forward", desc = "Go to next tag" }, - { "L", "Grapple cycle backward", desc = "Go to previous tag" }, + { "H", "Grapple cycle_tags next", desc = "Go to next tag" }, + { "L", "Grapple cycle_tags prev", desc = "Go to previous tag" }, }, }, ``` @@ -304,13 +306,13 @@ In general, the API is as follows: Where `opts` in the user command is a list of `value` arguments and `key=value` keyword arguments. For example, ```vim -:Grapple cycle forward scope=cwd +:Grapple cycle_tags next scope=cwd ``` Has the equivalent form ```lua -require("grapple").cycle("forward", { scope = "cwd" }) +require("grapple").cycle_tags("next", { scope = "cwd" }) ``` ### Grapple API @@ -419,19 +421,15 @@ require("grapple").select({ index = 3 }) -#### `Grapple.cycle` +#### `Grapple.cycle_tags` Cycle through and select the next or previous available tag for a given scope. -**API**: - -- `require("grapple").cycle(direction, opts)` -- `require("grapple").cycle_backward(opts)` -- `require("grapple").cycle_forward(opts)` +**API**: `require("grapple").cycle_tags(direction, opts)` Where: -- **`direction`**: `"backward"` | `"forward"` +- **`direction`**: `"next"` | `"prev"` - **`opts?`**: [`grapple.options`](#grappleoptions) (one of) **Note**: Starting tag is searched based on one of (in order): `index`, `name`, `path`, `buffer` @@ -441,10 +439,10 @@ Where: ```lua -- Cycle to the previous tagged file -require("grapple").cycle_backward() +require("grapple").cycle_tags("next") -- Cycle to the next tagged file -require("grapple").cycle_forward() +require("grapple").cycle_tags("prev") ``` diff --git a/lua/grapple.lua b/lua/grapple.lua index 9209060..e30a938 100644 --- a/lua/grapple.lua +++ b/lua/grapple.lua @@ -196,28 +196,73 @@ function Grapple.quickfix(opts) end end +---@param current_index? integer +---@param direction "next" | "prev" +---@param length integer +---@return integer +local function next_index(current_index, direction, length) + -- Fancy maths to get the next index for a given direction + -- 1. Change to 0-based indexing + -- 2. Perform index % container length, being careful of negative values + -- 3. Change back to 1-based indexing + -- stylua: ignore + current_index = ( + current_index + or direction == "next" and length + or direction == "prev" and 1 + ) - 1 + + local next_inc = direction == "next" and 1 or -1 + local next_idx = math.fmod(current_index + next_inc + length, length) + 1 + + return next_idx +end + ---Select the next available tag for a given scope ---By default, uses the current scope +---@deprecated Soft-deprecated in favour of Grapple.cycle_tags ---@param opts? grapple.options function Grapple.cycle_forward(opts) - Grapple.cycle("forward", opts) + Grapple.cycle_tags("next", opts) end ---Select the previous available tag for a given scope ---By default, uses the current scope +---@deprecated Soft-deprecated in favour of Grapple.cycle_tags ---@param opts? grapple.options function Grapple.cycle_backward(opts) - Grapple.cycle("backward", opts) + Grapple.cycle_tags("prev", opts) end -- Cycle through and select the next or previous available tag for a given scope. ---By default, uses the current scope +---@deprecated Soft-deprecated in favour of Grapple.cycle_tags ---@param direction "forward" | "backward" ---@param opts? grapple.options function Grapple.cycle(direction, opts) + Grapple.cycle_tags(direction, opts) +end + +-- Cycle through and select the next or previous available tag for a given scope. +---By default, uses the current scope +---@param direction "next" | "prev" | "previous" | "forward" | "backward" +---@param opts? grapple.options +function Grapple.cycle_tags(direction, opts) + local App = require("grapple.app") + local app = App.get() + opts = opts or {} - local app = require("grapple.app").get() + -- stylua: ignore + direction = direction == "forward" and "next" + or direction == "backward" and "prev" + or direction == "previous" and "prev" + or direction + + if not vim.tbl_contains({ "next", "prev" }, direction) then + return vim.notify(string.format("invalid direction: %s", direction), vim.log.levels.ERROR) + end + app:enter_without_save(opts.scope, function(container) if container:is_empty() then return @@ -226,20 +271,10 @@ function Grapple.cycle(direction, opts) local path, _ = extract_path(opts) opts.path = path - -- Fancy maths to get the next index for a given direction - -- 1. Change to 0-based indexing - -- 2. Perform index % container length, being careful of negative values - -- 3. Change back to 1-based indexing - local index = ( - container:find(opts) - or direction == "forward" and container:len() - or direction == "backward" and 1 - ) - 1 - local next_direction = direction == "forward" and 1 or -1 - local next_index = math.fmod(index + next_direction + container:len(), container:len()) + 1 + local index = next_index(container:find(opts), direction, container:len()) ---@diagnostic disable-next-line: redefined-local - local tag, err = container:get({ index = next_index }) + local tag, err = container:get({ index = index }) if not tag then return err end @@ -694,9 +729,7 @@ function Grapple.initialize() -- Lookup table of API functions and their available arguments local subcommand_lookup = { clear_cache = { args = { "scope" }, kwargs = {} }, - cycle = { args = { "direction" }, kwargs = use_kwargs }, - cycle_backward = { args = {}, kwargs = use_kwargs }, - cycle_forward = { args = {}, kwargs = use_kwargs }, + cycle_tags = { args = { "direction" }, kwargs = use_kwargs }, open_loaded = { args = {}, kwargs = { "all" } }, open_scopes = { args = {}, kwargs = {} }, open_tags = { args = {}, kwargs = window_kwargs }, @@ -717,13 +750,18 @@ function Grapple.initialize() -- Lookup table of arguments and their known values local argument_lookup = { all = { "true", "false" }, - direction = { "forward", "backward" }, + direction = { "next", "prev" }, scope = Util.sort(vim.tbl_keys(app.scope_manager.scopes), Util.as_lower), style = Util.sort(vim.tbl_keys(app.settings.styles), Util.as_lower), } -- API methods which are not actionable local excluded_subcmds = { + -- Deprecated + "cycle", + "cycle_backward", + "cycle_forward", + "define_scope", "delete_scope", "exists",