chentoast / marks.nvim

A better user experience for viewing and interacting with Vim marks.
MIT License
788 stars 40 forks source link

Option to view jumps #50

Open mikeslattery opened 2 years ago

mikeslattery commented 2 years ago

I love this plugin. It would also be nice if locations in the jump list were visible, so I could use <n>c-o and <n>c-i to jump directly to them.

I imagine they would show up as <n>o or <n>i (e.g. 2o is 2 prior locations, accessible as 2<c-o>). If multiple on a line, just show the lowest numbered one. Also, don't show if ' built-in is on same line as a jump (likely 1o).

The option could be jump_limit, which defaults to 0 (off) or something reasonably useful, like 4.

This could also be done with the change list, showing up as <n>, or <n>; (e.g. users could use 2g; to go to the 2; sign), but this is of less value. I'd imagine change_limit would default to 0.

chentoast commented 2 years ago

While I do think this idea would be useful, it does seem slightly out of scope for this plugin and would complicate the current refresh logic. I'm not necessarily opposed to putting such a feature in, but I would like to have more discussion about whether there exists another plugin that already does something similar. Also personally I might prefer to show jumps as <number>j, where <number> is the position in the jumplist.

mikeslattery commented 2 years ago

@chentau I agree with you that its scope is questionable. I looked and couldn't find any plugins that show jumps in the sign column. The reason I wrote this ticket is that built-in marks behave like jumps/changes and due to the lack of such a plugin.

Also personally I might prefer to show jumps as <number>j, where <number> is the position in the jumplist.

Sounds good. I used o and i so users wouldn't have to think which key to hit and I didn't want 3-character signs for backward jumps (e.g. -2j).

Your plugin has made me much more effective at movements, but I feel a little lost when using jumps (c-o/c-i); I tend to over/under shoot my intended location, or I go the wrong direction.

I've experimented with sign_*()and getjumplist() functions so I may just write a small vimscript autocmd for this myself separately, if you chose not to.

chentoast commented 2 years ago

I think that’s reasonable; it probably makes sense to include something like this if no other plugin implements this, given that a lot of the sign placing and refresh infrastructure is already implemented. I’m currently in a quite busy point in my life, so it may be a while before I get to it.

also I still need to think more about how exactly to display jumps. I feel like 1j is too easily confused with 1 filemark and the j mark on the same line. Ideally the signs for jumps would be some single width unicode characters.

mikeslattery commented 2 years ago

This seems to work:

diff --git a/lua/marks/mark.lua b/lua/marks/mark.lua
index 8c3607d..0b52f5b 100644
--- a/lua/marks/mark.lua
+++ b/lua/marks/mark.lua
@@ -321,10 +321,8 @@ function Mark:refresh(bufnr, force)

   -- first, remove all marks that were deleted
   for mark, _ in pairs(self.buffers[bufnr].placed_marks) do
-    if a.nvim_buf_get_mark(bufnr, mark)[1] == 0 then
     self:delete_mark(mark, false)
   end
-  end

   local mark
   local pos
@@ -368,6 +366,46 @@ function Mark:refresh(bufnr, force)
       self:register_mark(char, pos[2], pos[3], bufnr)
     end
   end
+
+  -- jumplist
+  winnr = a.nvim_get_current_win()
+  tabnr = a.nvim_get_current_tabpage()
+  jumplist = vim.fn.getjumplist(winnr, tabnr)
+  jumps = jumplist[1]
+  jump_pointer = jumplist[2]
+
+  -- future jumps, ctrl-i
+  for i = 1, 5 do
+    -- forward.  usually empty
+    index = jump_pointer + i + 1
+    if index <= #jumps then
+      jump = jumps[index]
+      label = 'i'
+      if i > 1 then
+        label = i .. label
+      end
+      if jump.lnum ~= 0 and (jump.bufnr == 0 or jump.bufnr == bufnr) and
+        (force or not cached_mark or jump.lnum ~= cached_mark.line) then
+        self:register_mark(label, jump.lnum, jump.col, jump.bufnr)
+      end
+    end
+
+    -- back history
+    index = jump_pointer - i + 1
+    if index > 0 then
+      jump = jumps[index]
+      label = 'o'
+      if i > 1 then
+        label = i .. label
+      end
+      cached_mark = self.buffers[bufnr].placed_marks[label]
+      if jump.lnum ~= 0 and (jump.bufnr == 0 or jump.bufnr == bufnr) and
+        (force or not cached_mark or jump.lnum ~= cached_mark.line) then
+        self:register_mark(label, jump.lnum, jump.col, jump.bufnr)
+      end
+    end
+  end
+
   return
 end

Needs more testing. I need to figure out what I've broken by removing 2 lines. I used the 2 character size signs (e.g. 2o), which I'll see if can use unicode to reduce to 1. I may need to remove arguments to getjumplist(). It's not DRY.

chentoast commented 2 years ago

Can you convert this to a pr and I’ll review it more closely? Alternatively I can take the lead on this if you don’t have time

mikeslattery commented 2 years ago

@chentau Maybe in a week or so as I am quite busy atm. As I said at the bottom of my comment, it needs more testing and has several minor issues.

mikeslattery commented 2 years ago

Improvements. Untested.

history_max = 5

winnr = a.nvim_get_current_win()
jumplist = vim.fn.getjumplist(winnr)
jumps = jumplist[1]
jump_pointer = jumplist[2]

-- ctrl-i  ( while i<0 )
operation = '\u{00ee}'
for i = math.max(history_max * -1, 1), math.min(history_max, #jump) do
  if i == 0 then
    -- switch to ctrl-o  ( while i>0 )
    operation = '\u{00f4}'
  else
    index = jump_pointer + i + 1
    jump = jumps[index]

    cached_mark = self.buffers[bufnr].placed_marks[label]
    if jump.lnum ~= 0 and (jump.bufnr == 0 or jump.bufnr == bufnr) and
      (force or not cached_mark or jump.lnum ~= cached_mark.line) then

      if i == -1 or i == 1 then
        label = i .. operation
      else
        label = operation
      end
      self:register_mark(label, jump.lnum, jump.col, jump.bufnr)
    end
  end
end

edit: removed the tabnr argument to getjumplist(). That didnt' seem to work correctly for me.