cbochs / cbochs/grapple.nvim

proposal: "frozen" file tags

Open
#118 8 comments 9 reactions 0 assignees View on GitHub
Dominant language
Lua
Stars
717
Forks
33
PR merge metrics
No merged PRs in 30d

Description

## Frozen Tags

### Context

I have seen this suggestion/idea pop up many times across many different plugins which are attempting to make file/buffer navigation easier. It's even been requested here (#58 #65 #66). This seems to be some way to bridge the gap between harpoon-like file marking and [`:h mark-motions`](https://neovim.io/doc/user/motion.html#mark-motions).

For those unfamiliar, Vim marks allow you to do two things:
* Use `'a` (lowercase) marks to mark a _specific_ location in some file, globally
* Use `'A` (uppercase) to mark a _specific_ location in a _specific_ file, globally

Grapple compliments the above system by allowing users to "tag" a file, but _not_ a specific location. Rather, the cursor is tracked and will be restored when the tag is selected. In addition, tags are scoped _per-project_ instead of globally.

There is a gap, however. What if you want that tag to stop updating, temporarily? What if you want to tag a specific location, but keep it local to the project you're working on?

### Proposal

I would like to propose the following: frozen tags. These would be similar to regular tags, but their cursor position would never update. In addition to never updating, there would be more than one allowed per file.

Although it would be recommended that a frozen tag be created with a name, it would **not be required**. Selection behaviour would still always prefer the non-frozen tag, if not specified.

**NOTE: default behaviour would remain the same. Non-frozen (dynamic) tags would still be restricted to one-per-filepath and track the last known cursor location.**

For example, creating a frozen tag would look something like this:

```lua
require("grapple").tag({ buffer = 0, name = "a", frozen = true })
```

And to select a frozen tag:

```lua
require("grapple").select({ name = "a", frozen = true })
```

You could convert a non-frozen tag into a frozen tag, creating a new file tag if it doesn't exist (this would likely be a simple wrapper around `Grapple.tag`):

```lua
require("grapple").freeze({ buffer = 0 })
```

You could convert a frozen tag into a non-frozen tag, overwriting (or not) an existing non-frozen tag if one does exist (again, this would likely be a simple wrapper around `Grapple.tag`):

```lua
require("grapple").unfreeze({ buffer = 0, overwrite = true })
```

### Implementation

The implementation is a bit fuzzy right now, but `grapple.tag` would look a bit like this:

```lua
---@class grapple.tag
---@field path string absolute path
---@field name string | nil (optional) tag name
---@field cursor integer[] (1, 0)-indexed cursor position
---@field frozen boolean
```

And the `grapple.tag_container` would need to accommodate more than one file per tag. That would require some refactor work, but not be too difficult.

The UI would need some experimentation to ensure it doesn't distract too much, add more overhead, or feel out-of-place.

Contributor guide

No contributing guide indexed for this repository

Research direction

Start by reviewing the grapple.tag API and the grapple.tag_container model described in the proposal, then examine how the existing UI selects and displays tags. Determine the required behavior for frozen tags, conversion between frozen and non-frozen tags, multiple tags per file, and selection precedence; the issue is complete only when the data model, APIs, and UI behavior are defined and implemented consistently.

Written by the indexing model from the issue text.

Assessment

Tech stack
lua
Domain
tooling
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Needs clarification
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.