grapple.nvim icon indicating copy to clipboard operation
grapple.nvim copied to clipboard

proposal: "frozen" file tags

Open cbochs opened this issue 11 months ago • 7 comments

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.

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:

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

And to select a frozen tag:

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):

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):

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:

---@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.

cbochs avatar Mar 15 '24 17:03 cbochs

To summarize this

  • Allow the option to jump to a specific line + file
    • (currently I believe the default is to go to the previous cursor position in the file). But I think grapple does allow you to save the current cursor position?
  • Allow "naming" a position, similar to marks
    • If naming is based on single letters, maybe text motions could be implemented for these frozen tags?
  • Allow multiple tags per file
  • The UI view of grapple would likely need an update, idk, maybe a tree view?
    • some/file.txt
      • [a] - line 10 - some frozen tag
      • [b] - line 15 - another frozen tag
      • [c] - line 21 - etc
      • ...

Like that?

ColinKennedy avatar Mar 16 '24 05:03 ColinKennedy

@ColinKennedy Thanks for taking the time to summarize. To clarify:

I think grapple does allow you to save the current cursor position?

Correct. A cursor position can be specified with cursor = { x, y }, but will be tracked and overridden to the previous cursor position by default. With the proposed changes, frozen = true will ensure a set cursor position is not tracked or overridden.

Allow "naming" a position, similar to marks

Correct. Grapple already allows names with (i.e. name = "a"). However, to push this point a bit, I am leaning towards having named tags remain local to the project, not buffer. That means, for example:

  • Allowed: frozen tag with name a on file A, frozen tag with name b on file A
  • Allowed: frozen tag with name a on file A, frozen tag with name b on file B
  • Disallowed: frozen tag with name a on file A, frozen tag with name a on file B

The UI view of grapple would likely need an update, idk, maybe a tree view?

A tree view would likely complicate things. It could likely remain a list view with a the cursor as an additional detail. For example (mockup),

image

cbochs avatar Mar 17 '24 20:03 cbochs

A list view would work, though it would be nice if users can customize where each piece of info goes per-row. For example I would prefer to have it be mark | relative/project path | (row, column) whereas others might want (row, column) | mark | relative/project path as your mockup shows

ColinKennedy avatar Mar 17 '24 22:03 ColinKennedy

While I agree that providing formatting options would be useful, I think that might be better suited as a later feature. For now, there are already a couple options available in the settings (see style and name_pos).

cbochs avatar Mar 18 '24 01:03 cbochs

I find myself looking into this issue every couple of months because it'll be so nice when implemented!

tkivela avatar Jun 01 '24 12:06 tkivela

I thought grapple.nvim already remembered cursor position (coming from reading this issue: https://github.com/cbochs/grapple.nvim/issues/72). Looking forward for this proposal! :+1:

serranomorante avatar Aug 28 '24 22:08 serranomorante

Heh PERFECT timing. I was at it yesterday to hack about in the grapple code to get exactly this feature going. I haven't yet been successful, and your implementation sounds great, because I was just toying with the autocmds.

in #72 there is a workaround to remove the autocmds and to hodge podge some things, but it's not ideal, because if you remove the auto commands, you never get a cursor location saved since it's ONLY saved via autocmd for me, never when I first create the tag.

KiLLeRRaT avatar Aug 29 '24 21:08 KiLLeRRaT