Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/cbochs/grapple.nvim
Neovim plugin for tagging important files
https://github.com/cbochs/grapple.nvim
neovim neovim-plugin nvim plugin
Last synced: about 1 month ago
JSON representation
Neovim plugin for tagging important files
- Host: GitHub
- URL: https://github.com/cbochs/grapple.nvim
- Owner: cbochs
- License: mit
- Created: 2022-10-24T04:18:27.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2024-04-11T01:51:38.000Z (about 1 month ago)
- Last Synced: 2024-04-11T04:00:12.031Z (about 1 month ago)
- Topics: neovim, neovim-plugin, nvim, plugin
- Language: Lua
- Homepage:
- Size: 575 KB
- Stars: 424
- Watchers: 4
- Forks: 13
- Open Issues: 8
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE
Lists
- awesome-neovim - cbochs/grapple.nvim - Provides tagging, cursor tracking, and immediate navigation to important project files. (Marks / PHP)
- awesome-stars - cbochs/grapple.nvim - Neovim plugin for tagging important files (Lua)
- my-awesome-stars - cbochs/grapple.nvim - Neovim plugin for tagging important files (Lua)
- awesome-stars - grapple.nvim
- awesome-stars - cbochs/grapple.nvim - Neovim plugin for tagging important files (Lua)
- awesome-neovim - cbochs/grapple.nvim - Provides tagging, cursor tracking, and immediate navigation to important project files. (Marks / Markdown and LaTeX)
- awesome-neovim - cbochs/grapple.nvim - Provides tagging, cursor tracking, and immediate navigation to important project files. (Marks / Markdown and LaTeX)
README
# Grapple.nvim
Theme: [kanagawa](https://github.com/rebelot/kanagawa.nvim)
Showcase
![grapple-showcase](https://github.com/cbochs/grapple.nvim/assets/2467016/61cca5ae-26af-411f-904f-3eb7735a50c4)
## Introduction
Grapple is a plugin that aims to provide immediate navigation to important files. See the [quickstart](#quickstart) section to get started.
## Goals
While Grapple shares similarities to Harpoon (and other file navigation plugins), it aims to differentiate itself in the following ways:
- Frictionless first time configuration and usage (no setup, just define your keymaps and go)
- Fine-grained customization of project spaces in the form of project [scopes](#scopes)
- Improved tag and scope management UI to compliment Grapple's tag/select navigation model
- Ensure Grapple's public API is well-documented## Features
- **Persistent** tags on file paths to track and restore cursor location
- **Scoped** tags for fine-grained, per-project tagging (i.e. git branch)
- **Rich** well-defined [Grapple](#grapple-api) and [Scope](#scope-api) APIs
- **Toggleable** windows to manage tags and scopes as a regular vim buffer
- **Integration** with [telescope.nvim](#telescope)
- **Integration** with [portal.nvim](https://github.com/cbochs/portal.nvim) for additional jump options## Requirements
- [Neovim >= 0.9](https://github.com/neovim/neovim/releases/tag/v0.9.0)
- [nvim-web-devicons](https://github.com/nvim-tree/nvim-web-devicons) (optional)## Quickstart
- [Install](#installation) Grapple.nvim using your preferred package manager
- Add a keybind to `tag`, `untag`, or `toggle` a path. For example,```lua
-- Lua
vim.keymap.set("n", "m", require("grapple").toggle)
vim.keymap.set("n", "M", require("grapple").toggle_tags)-- User command
vim.keymap.set("n", "1", "Grapple select index=1")
```**Next steps**
- Check out the [example setups](#example-setups)
- Check out the default [settings](#settings)
- View your [tags](#tags-window) with `:Grapple toggle_tags`
- Choose a [scope](#scopes-window) with `:Grapple toggle_scopes`
- Manage your [loaded scopes](#loaded-scopes-window) with `:Grapple toggle_loaded`
- Add a [statusline component](#statusline)
- Explore the [Grapple](#grapple-api) and [Scope](#scope-api) APIs## Installation
lazy.nvim
```lua
{
"cbochs/grapple.nvim",
dependencies = {
{ "nvim-tree/nvim-web-devicons", lazy = true }
},
}
```packer
```lua
use {
"cbochs/grapple.nvim",
requires = { "nvim-tree/nvim-web-devicons" }
}
```vim-plug
```vim
Plug "nvim-tree/nvim-web-devicons"
Plug "cbochs/grapple.nvim"
```## Example Setups
Note, these examples assume you are using the [lazy.nvim](https://github.com/folke/lazy.nvim) package manager.
Recommended
```lua
{
"cbochs/grapple.nvim",
opts = {
scope = "git", -- also try out "git_branch"
},
event = { "BufReadPost", "BufNewFile" },
cmd = "Grapple",
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" },
},
},
```Harpoon
Example configuration similar to [harpoon.nvim](https://github.com/ThePrimeagen/harpoon) (based off of this [example setup](https://github.com/ThePrimeagen/harpoon/tree/harpoon2?tab=readme-ov-file#basic-setup)).
```lua
{
"cbochs/grapple.nvim",
opts = {
scope = "git", -- also try out "git_branch"
icons = false, -- setting to "true" requires "nvim-web-devicons"
status = false,
},
keys = {
{ "a", "Grapple toggle", desc = "Tag a file" },
{ "", "Grapple toggle_tags", desc = "Toggle tags menu" },{ "", "Grapple select index=1", desc = "Select first tag" },
{ "", "Grapple select index=2", desc = "Select second tag" },
{ "", "Grapple select index=3", desc = "Select third tag" },
{ "", "Grapple select index=4", desc = "Select fourth tag" },{ "", "Grapple cycle_tags next", desc = "Go to next tag" },
{ "", "Grapple cycle_tags prev", desc = "Go to previous tag" },
},
},
```Arrow
Example configuration similar to [arrow.nvim](https://github.com/otavioschwanck/arrow.nvim/tree/master).
```lua
{
"cbochs/grapple.nvim",
dependencies = {
{ "nvim-tree/nvim-web-devicons" }
},
opts = {
scope = "git_branch",
icons = true,
quick_select = "123456789",
},
keys = {
{ ";", "Grapple toggle_tags", desc = "Toggle tags menu" },{ "", "Grapple toggle", desc = "Toggle tag" },
{ "H", "Grapple cycle_tags next", desc = "Go to next tag" },
{ "L", "Grapple cycle_tags prev", desc = "Go to previous tag" },
},
},
```## Settings
The following are the default settings for Grapple. **Setup is not required**, but settings may be overridden by passing them as table arguments to the `Grapple.setup` function.
Default Settings
```lua
require("grapple").setup({
---Grapple save location
---@type string
save_path = vim.fs.joinpath(vim.fn.stdpath("data"), "grapple"),---Default scope to use when managing Grapple tags
---For more information, please see the Scopes section
---@type string
scope = "git",---User-defined scopes or overrides
---For more information about scopes, please see the Scope API section
---@type grapple.scope_definition[]
scopes = {},---Default scopes provided by Grapple
---For more information about default scopes, please see the Scopes section
---Disable by setting scope to "false". For example, { lsp = false }
---@type table
default_scopes = { ... }---Show icons next to tags or scopes in Grapple windows
---Requires "nvim-tree/nvim-web-devicons"
---@type boolean
icons = true,---Highlight the current selection in Grapple windows
---Also, indicates when a tag path does not exist
---@type boolean
status = true,---Position a tag's name should be shown in Grapple windows
---@type "start" | "end"
name_pos = "end",---How a tag's path should be rendered in Grapple windows
--- "relative": show tag path relative to the scope's resolved path
--- "basename": show tag path basename and directory hint
---@type "basename" | "relative"
style = "relative",---A string of characters used for quick selecting in Grapple windows
---An empty string or false will disable quick select
---@type string | boolean
quick_select = "123456789",---Default command to use when selecting a tag
---@type fun(path: string)
command = vim.cmd.edit,---Time limit used for pruning unused scope (IDs). If a scope's save file
---modified time exceeds this limit, then it will be deleted when a prune
---requested. Can be an integer (in seconds) or a string time limit
---(e.g. "30d" or "2h" or "15m")
---@type integer | string
prune = "30d",---User-defined tags title function for Grapple windows
---By default, uses the resolved scope's ID
---@type fun(scope: grapple.resolved_scope): string?
tag_title = nil,---User-defined scopes title function for Grapple windows
---By default, renders "Grapple Scopes"
---@type fun(): string?
scope_title = nil,---User-defined loaded scopes title function for Grapple windows
---By default, renders "Grapple Loaded Scopes"
---@type fun(): string?
loaded_title = nil,---Additional window options for Grapple windows
---See :h nvim_open_win
---@type grapple.vim.win_opts
win_opts = {
-- Can be fractional
width = 80,
height = 12,
row = 0.5,
col = 0.5,relative = "editor",
border = "single",
focusable = false,
style = "minimal",
title_pos = "center",-- Custom: fallback title for Grapple windows
title = "Grapple",-- Custom: adds padding around window title
title_padding = " ",
},
})
```## Usage
In general, the API is as follows:
**Lua**: `require("grapple").{method}(...)`
**Command**: `:Grapple [method] [opts...]`
Where `opts` in the user command is a list of `value` arguments and `key=value` keyword arguments. For example,
```vim
:Grapple cycle_tags next scope=cwd
```Has the equivalent form
```lua
require("grapple").cycle_tags("next", { scope = "cwd" })
```### Grapple API
Grapple API and Examples
#### `Grapple.tag`
Create a grapple tag.
**Command**: `:Grapple tag [buffer={buffer}] [path={path}] [index={index}] [name={name}] [scope={scope}]`
**API**: `require("grapple").tag(opts)`
**`opts?`**: [`grapple.options`](#grappleoptions)
- **`buffer?`**: `integer` (default: `0`)
- **`path?`**: `string`
- **`index?`**: `integer`
- **`name?`**: `string`
- **`scope?`**: `string`**Note**: only one tag can be created _per scope per file_. If a tag already exists for the given file or buffer, it will be overridden with the new tag.
Examples
```lua
-- Tag the current buffer
require("grapple").tag()-- Tag a file by its file path
require("grapple").tag({ path = "some_file.lua" })-- Tag the current buffer in a different scope
require("grapple").tag({ scope = "global" })-- Tag the file path under the cursor
require("grapple").tag({ path = "" })
```#### `Grapple.untag`
Remove a Grapple tag.
**API**: `require("grapple").untag(opts)`
**`opts?`**: [`grapple.options`](#grappleoptions) (one of)
**Note**: Tag is removed based on one of (in order): `index`, `name`, `path`, `buffer`
Examples
```lua
-- Remove a tag on the current buffer
require("grapple").untag()-- Remove a tag on a file
require("grapple").untag({ file_path = "{file_path}" })-- Remove a tag on the current buffer in a different scope
require("grapple").untag({ scope = "global" })
```#### `Grapple.toggle`
Toggle a Grapple tag.
**API**: `require("grapple").toggle(opts)`
**`opts?`**: [`grapple.options`](#grappleoptions)
Examples
```lua
-- Toggle a tag on the current buffer
require("grapple").toggle()
```#### `Grapple.select`
Select a Grapple tag.
**API**: `require("grapple").select(opts)`
**`opts?`**: [`grapple.options`](#grappleoptions) (one of)
**Note**: Tag is selected based on one of (in order): `index`, `name`, `path`, `buffer`
Examples
```lua
-- Select the third tag
require("grapple").select({ index = 3 })
```#### `Grapple.cycle_tags`
Cycle through and select the next or previous available tag for a given scope.
**API**: `require("grapple").cycle_tags(direction, opts)`
Where:
- **`direction`**: `"next"` | `"prev"`
- **`opts?`**: [`grapple.options`](#grappleoptions) (one of)**Note**: Starting tag is searched based on one of (in order): `index`, `name`, `path`, `buffer`
Examples
```lua
-- Cycle to the previous tagged file
require("grapple").cycle_tags("next")-- Cycle to the next tagged file
require("grapple").cycle_tags("prev")
```#### `Grapple.unload`
Unload tags for a give (scope) name or loaded scope (id).
**API**: `require("grapple").unload(opts)`
**`opts?`**: `table`
- **`scope?`**: `string` scope name (default: `settings.scope`)
- **`id?`**: `string` the ID of a resolved scopeExamples
```lua
-- Unload the current scope
require("grapple").unload()-- Unload a scope (dynamic)
require("grapple").unload({ scope = "git" })-- Unload a specific resolved scope ID
require("grapple").unload({ id = "~/git" })
```#### `Grapple.reset`
Reset tags for a given (scope) name or loaded scope (id).
**API**: `require("grapple").reset(opts)`
**`opts?`**: `table`
- **`scope?`**: `string` scope name (default: `settings.scope`)
- **`id?`**: `string` the ID of a resolved scopeExamples
```lua
-- Reset the current scope
require("grapple").reset()-- Reset a scope (dynamic)
require("grapple").reset({ scope = "git" })-- Reset a specific resolved scope ID
require("grapple").reset({ id = "~/git" })
```#### `Grapple.prune`
Prune save files based on their last modified time.
**API**: `require("grapple").prune(opts)`
**`opts?`**: `table`
- **`limit?`**: `integer` | `string` modified time limit (default: `settings.prune`)
Examples
```lua
-- Prune using the default time limit
require("grapple").prune()-- Prune longer than 30 days
require("grapple").prune({ limit = "30d" })-- Prune longer than 6 hours
require("grapple").prune({ limit = "6h" })-- Prune longer than 15 minutes
require("grapple").prune({ limit = "15m" })-- Prune longer than 120 seconds
require("grapple").prune({ limit = "120s" })
require("grapple").prune({ limit = 120 })
```#### `Grapple.quickfix`
Open the quickfix window populated with paths from a given scope
**API**: `require("grapple").quickfix(opts)`
**`opts?`**: `table`
- **`scope?`**: `string` scope name (default: `settings.scope`)
- **`id?`**: `string` the ID of a resolved scopeExamples
```lua
-- Open the quickfix window for the current scope
require("grapple").quickfix()-- Open the quickfix window for a specified scope
require("grapple").quickfix("global")
```#### `Grapple.exists`
Return if a tag exists. Used for statusline components
**API**: `require("grapple").exists(opts)`
**`returns`**: `boolean`
**`opts?`**: [`grapple.options`](#grappleoptions) (one of)
**Note**: Tag is searched based on one of (in order): `index`, `name`, `path`, `buffer`
Examples
```lua
-- Check whether the current buffer is tagged or not
require("grapple").exists()-- Check for a tag in a different scope
require("grapple").exists({ scope = "global" })
```#### `Grapple.find`
Search for a tag in a given scope.
**API**: `require("grapple").find(opts)`
**`opts?`**: [`grapple.options`](#grappleoptions) (one of)
**`returns`**: [`grapple.tag`](#grappletag) | `nil`, `string?` error
**Note**: Tag is searched based on one of (in order): `index`, `name`, `path`, `buffer`
Examples
```lua
-- Search for a tag by index in the current scope
require("grapple").find({ index = 1 })-- Search for a named tag in a different scope
require("grapple").find({ name = "bob", scope = "global" })
```### Scope API
Scopes API and Examples
#### `Grapple.define_scope`
Create a user-defined scope.
**API**: `require("grapple").define_scope(definition)`
**`definition`**: [`grapple.scope_definition`](#grapplescope_definition)
Examples
For more examples, see [settings.lua](./lua/grapple/settings.lua)
```lua
-- Define a scope during setup
require("grapple").setup({
scope = "cwd_branch",scopes = {
{
name = "cwd_branch",
desc = "Current working directory and git branch",
fallback = "cwd",
cache = {
event = { "BufEnter", "FocusGained" },
debounce = 1000, -- ms
},
resolver = function()
local git_files = vim.fs.find(".git", {
upward = true,
stop = vim.loop.os_homedir(),
})if #git_files == 0 then
return
endlocal root = vim.loop.cwd()
local result = vim.fn.system({ "git", "symbolic-ref", "--short", "HEAD" })
local branch = vim.trim(string.gsub(result, "\n", ""))local id = string.format("%s:%s", root, branch)
local path = rootreturn id, path
end,
}
}
})-- Define a scope outside of setup
require("grapple").define_scope({
name = "projects",
desc = "Project directory"
fallback = "cwd",
cache = { event = "DirChanged" },
resolver = function()
local projects_dir = vim.fs.find("projects", {
upwards = true,
stop = vim.loop.os_homedir()
})if #projects_dir == 0 then
return nil, nil, "Not in projects dir"
endlocal path = projects_dir[1]
local id = path
return id, path, nil
end
})-- Use the scope
require("grapple").use_scope("projects")
```#### `Grapple.delete_scope`
Delete a default or user-defined scope.
**API**: `require("grapple").delete_scope(scope)`
**`scope`**: `string` scope name
**`returns`**: `string?` error
#### `Grapple.use_scope`
Change the currently selected scope.
**API**: `require("grapple").use_scope(scope)`
**`scope`**: `string` scope name
Examples
```lua
-- Clear the cached value (if any) for the "git" scope
require("grapple").use_scope("git_branch")
```#### `Grapple.clear_cache`
Clear any cached value for a given scope.
**API**: `require("grapple").clear_cache(scope)`
**`scope?`**: `string` scope name (default: `settings.scope`)
Examples
```lua
-- Clear the cached value for the initial working directory scope
require("grapple").clear_cache("static")
```## Tags
A **tag** is a persistent tag on a file path or URL. It is a means of indicating a file you want to return to. When a file is tagged, Grapple will save your cursor location so that when you jump back, your cursor is placed right where you left off. In a sense, tags are like file-level marks ([`:h mark`](https://neovim.io/doc/user/motion.html#mark-motions)).
Once a tag has been added to a [scope](#scopes), it may be selected by index or name, cycled through, or even jumped to using plugins such as [portal.nvim](https://github.com/cbochs/portal.nvim).
## Scopes
A **scope** is a means of namespacing tags to a specific project. Scopes are resolved dynamically to produce a unique identifier for a set of tags (i.e. a root directory). This identifier determines where tags are created and deleted. **Note**, different scopes may resolve the same identifier (i.e. `lsp` and `git` scopes may share the same root directory).
Scopes can also be _cached_. Each scope may define a set of `events` and/or `patterns` for an autocommand ([`:h autocmd`](https://neovim.io/doc/user/autocmd.html)), an `interval` for a timer, or to be cached indefinitely (unless invalidated explicitly). Some examples of this are the `cwd` scope which only updates on `DirChanged`.
The following scopes are made available by default:
- `global`: tags are scoped to a global namespace
- `static`: tags are scoped to neovim's initial working directory
- `cwd`: tags are scoped to the current working directory
- `lsp`: tags are scoped to the root directory of the current buffer's attached LSP server, **fallback**: `cwd`
- `git`: tags are scoped to the current git repository, **fallback**: `cwd`
- `git_branch`: tags are scoped to the current git directory **and** git branch, **fallback**: `cwd`It is also possible to create your own **custom scope**. See the [Scope API](#scope-api) for more information.
Examples
```lua
-- Use a builtin scope
require("grapple").setup({
scope = "git_branch",
})-- Define and use a custom scope
require("grapple").setup({
scope = "custom",scopes = {
name = "custom",
fallback = "cwd",
cache = { event = "DirChanged" },
resolver = function()
local path = vim.env.HOME
local id = path
return id, path
end
}
})-- Disable a default scope
-- Note: be careful to disable default scopes that are used as fallbacks
require("grapple").setup({
default_scopes = {
lsp = false
}
})
```## Grapple Windows
Popup windows are made available to enable easy management of tags and scopes. The opened buffer is given its own syntax (`grapple`) and file type (`grapple`) and can be modified like a regular buffer; meaning items can be selected, modified, reordered, or deleted with well-known vim motions. The floating window can be toggled or closed with either `q` or ``.
### Tags Window
Open a floating window with all the tags for a given scope. This buffer is modifiable. Several actions are available by default:
- **Selection** (``): select the tag under the cursor
- **Split (horizontal)** (``): select the tag under the cursor (`split`)
- **Split (vertical)** (`|`): select the tag under the cursor (`vsplit`)
- **Quick select** (default: `1-9`): select the tag at a given index
- **Deletion**: delete a line to delete the tag
- **Reordering**: move a line to move a tag
- **Renaming** (`R`): rename the tag under the cursor
- **Quickfix** (``): send all tags to the quickfix list ([`:h quickfix`](https://neovim.io/doc/user/quickfix.html))
- **Go up** (`-`): navigate up to the [scopes window](#scopes-window)
- **Help** (`?`): open the help window**API**:
- `require("grapple").open_tags(opts)`
- `require("grapple").toggle_tags(opts)`**`opts?`**: `table`
- **`scope?`**: `string` scope name
- **`id?`**: `string` the ID of a resolved scope
- **`style?`**: the [style](#settings) to use for the tags windowExamples
```lua
-- Open the tags window for the current scope
require("grapple").open_tags()-- Open the tags window for a different scope
require("grapple").open_tags("global")
```### Scopes Window
Open a floating window with all defined scopes. This buffer is not modifiable. Some basic actions are available by default:
- **Selection** (``): open the [tags window](#tags-window) for the scope under the cursor
- **Quick select** (default: `1-9`): open the tags window for the scope at a given index
- **Change** (``): change the current scope to the one under the cursor
- **Go up** (`-`): navigate across to the [loaded scopes window](#loaded-scopes-window)
- **Toggle** (`g.`): toggle showing both hidden and unhidden scopes
- **Help** (`?`): open the help window**API**:
- `require("grapple").open_scopes()`
- `require("grapple").toggle_scopes()`Examples
```lua
-- Open the scopes window
require("grapple").open_scopes()
```### Loaded Scopes Window
Open a floating window with all loaded scope IDs. This buffer is not modifiable. Some basic actions are available by default:
- **Selection** (``): open the [tags window](#tags-window) for the loaded scope ID under the cursor
- **Quick select** (default: `1-9`): open tags window for the loaded scope ID at a given index
- **Unload** (`x`): unload the tags for the scope ID under the cursor
- **Deletion** (`X`): reset the tags for the scope ID under the cursor
- **Go up** (`-`): navigate across to the [scopes window](#scopes-window)
- **Toggle** (`g.`): toggle showing both loaded and unloaded scope IDs
- **Help** (`?`): open the help window**API**:
- `require("grapple").open_loaded(opts)`
- `require("grapple").toggle_loaded(opts)`**`opts?`**: `table`
- **`all`**: `boolean` (default: `false`)
Examples
```lua
-- Open the loaded scopes window, show only loaded scopes
require("grapple").open_loaded()-- Open the loaded scopes window, show both loaded and unloaded scopes
require("grapple").open_loaded({ all = true })
```### Window Highlights
| Highlight | Default Link | Style | Used in |
| ---------------- | ----------------- | ---------- | ------------------------------- |
| `GrappleBold` | N/A | `gui=bold` | Scopes window for scope names |
| `GrappleHint` | `Comment` | N/A | Tags window for directory hints |
| `GrappleName` | `DiagnosticHint` | N/A | Tags window for tag name |
| `GrappleNoExist` | `DiagnosticError` | N/A | Tags window for tag status |
| `GrappleCurrent` | `SpecialChar` | `gui=bold` | All windows for current status |## Persistent State
Grapple saves all scopes to a common directory. The default directory is named `grapple` and lives in Neovim's `"data"` directory ([`:h standard-path`](https://neovim.io/doc/user/starting.html#standard-path)). Each scope will be saved as its own individually serialized JSON blob.
By default, no scopes are loaded on startup. When `require("grapple").setup()` is called, the default scope will be loaded. Otherwise, scopes will be loaded on demand.
## Integrations
### Telescope
You can use [telescope.nvim](https://github.com/nvim-telescope/telescope.nvim) to search through your tagged files instead of the built in popup windows.
Load the extension with
```lua
require("telescope").load_extension("grapple")
```Then use this command to see the grapple tags for the project in a telescope window
```vim
:Telescope grapple tags
```### Statusline
A statusline component can be easily added to show whether a buffer is tagged.
**API**:
- `require("grapple").statusline(opts)`
**`opts?`**: `grapple.statusline.options` (default: `settings.statusline`)
- **`icon`**: `string` (default: `""`)
- **`active`**: `string` (default: `[%s]`)
- **`inactive`**: `string` (default: `" %s"`)
- **`include_icon`**: `boolean` (default: `true`)**Also available**:
- `require("grapple").name_or_index()`
- `require("grapple").exists()`
- `require("grapple").find()`
- `require("grapple").tags()`Examples
```lua
require("grapple").statusline()
-- Returns " [1] 2 3 4"require("grapple").name_or_index()
-- Returns "1" or "bob"-- Modify the statusline options
require("grapple").setup({
statusline = {
icon = "G",
active = "|%s|",
inactive = " %s "
}
})
```#### Lualine Component
Snippet
Screenshot```lua
require("lualine").setup({
sections = {
lualine_b = { "grapple" }
}
})
``````lua
require("lualine").setup({
sections = {
lualine_b = {
{
function()
return require("grapple").name_or_index()
end,
cond = function()
return package.loaded["grapple"] and require("grapple").exists()
end
}
}
}
})
```## Grapple Types
Type Definitions
### `grapple.options`
Options available for most top-level tagging actions (e.g. tag, untag, select, toggle, etc).
**Type**: `table`
- **`buffer`**: `integer` (default: `0`)
- **`path`**: `string` file path or `` (overrides `buffer`)
- **`name`**: `string` tag name
- **`index`**: `integer` tag insertion or deletion index (default: end of list)
- **`scope`**: `string` scope name (default `settings.scope`)### `grapple.tag`
Data object for a tagged file.
**Type**: `table`
- **`path`**: `string` absolute file path
- **`name`**: `string` (optional) tag name
- **`cursor`**: `integer[]` (1, 0)-indexed cursor position### `grapple.cache.options`
Options available for defining how a scope should be cached. Using the value of `true` will indicate a value should be cached indefinitely and is equivalent to providing an empty set of options (`{}`).
**Type**: `table` | `boolean`
- **`event?`**: `string` | `string[]` autocmd event ([`:h autocmd`](https://neovim.io/doc/user/autocmd.html))
- **`pattern?`**: `string` autocmd pattern, useful for `User` events
- **`interval?`**: `integer` timer interval
- **`debounce?`**: `integer` debounce interval### `grapple.scope_definition`
Used for defining new scopes.
**Type**: `table`
- **`name`**: `string` scope name
- **`resolver`**: [`grapple.scope_resolver`](#grapplescope_resolver)
- **`desc?`**: `string` scope description (default: `""`)
- **`force?`**: `boolean` scope fallback
- **`fallback?`**: `string` fallback scope name
- **`cache?`**: [`grapple.cache.options`](#grapplecacheoptions) | `boolean`
- **`priority?`**: `integer` scope priority, higher scopes are loaded first
- **`hidden?`**: `boolean` do not show the scope in the [Scopes Window](#scopes-window)**Note**: Scopes are given a `priority` based on their fallback ordering. By default, scopes without a fallback are given a priority of `1000`; scopes with a fallback, but are also fallbacks themselves, are given a priority of `100`; and all other scopes are given a priority of `1`. Higher priority scopes are loaded first. This can be overridden by setting a scope's `priority` manually in the [settings](#settings).
### `grapple.scope_resolver`
Used for defining new scopes. Must return a tuple of `(id, path, err)`. If successful, an `id` must be provided with an optional absolute path `path`. If unsuccessful, `id` must be `nil` with an optional `err` explaining what when wrong.
**Type**: `function`
**Returns**: `string? id, string? path, string? err`
### `grapple.resolved_scope`
Result from observing a scope at a point in time.
**Type** `class`
- **`name`**: `string` scope name
- **`id`**: `string` resolved scope ID
- **`path`**: `string` | `nil` resolved scope path
- **`:tags()`**: returns all tags for the given ID### Contributors
Thanks to these wonderful people for their contributions!
## Inspiration and Thanks
- ThePrimeagen's [harpoon](https://github.com/ThePrimeagen/harpoon)
- stevearc's [oil.nvim](https://github.com/stevearc/oil.nvim)