[Proposal] New motion command: `binsearch` for binary-search-style line navigation

Hi fellow Vim enthusiasts,

I'd like to propose a new motion command for Vim: `binsearch`, which facilitates precise cursor movement in **normal** and **visual** modes using a binary search approach.

---

###  Problem

Currently, if a user wants to jump to a specific line **visibly** on screen, they must resort to one of the following:

1. Use `H`, `M`, `L`, and then mash `j`/`k`.
2. Use `<n>G`, which requires knowing the exact line number (often a large number).
3. Use `/pattern`, which may jump thousands of lines off-target.

All of these are either cumbersome, error-prone, or break concentration.

---

###  Proposal: `binsearch` Motion

* **Trigger keys**:

  * `Ctrl-Q`: Begin or reset binary search session.
  * `Ctrl-J`: Move downward (toward lower bound).
  * `Ctrl-K`: Move upward (toward upper bound).

* **How it works**:

  * Pressing `Ctrl-Q` starts a binary search session. It sets the upper and lower bounds to the **top-most** and **bottom-most** visible lines.   * Pressing `Ctrl-K` or `Ctrl-J` then performs binary search: the cursor jumps to the midpoint between the current position and the respective bound.
  * Each jump updates the appropriate bound.
  * Pressing `Ctrl-Q` again resets the session.

* **Example use case**:

```
line 1 aaa...
line 10 bbb... <-- Ctrl-J
line 20 bbb... <-- Ctrl-K
line 30 ccc... <-- <cursor>
line 40 ddd...
line 41 ...
```

To reach line 30 directly, only **4 keypresses** may be needed — a big win in both speed and ergonomics. Because of binary search's logarithmic efficiency, it converges in a small number of steps (e.g., \~5 steps for 52 lines of height).

---

###  Implementation Notes

* Patch introduces `nv_binsearch()` in `src/normal.c`, and the command is registered in `src/nv_cmds.h`. * Documentation is added to `/runtime/doc/index.txt` and `/runtime/doc/motion.txt`. * This implementation is **functional** and works in **normal** and **visual** modes. Cursor updates are immediate so visual selections respond naturally.
* Patch file is attached to this email

---

###  Alternatives Considered

* **Operator-pending mode** is not currently supported, though this can be added by delaying motion until the final step (similar to how `G` behaves). * A plugin was considered, but this functionality touches core motion behavior and is best integrated natively. Also, plugins increase setup complexity, especially for new users.

---

###  Tested

* Built and tested on Ubuntu — works as expected.

---

Any feedback on this command or implementation direction would be highly appreciated. If there is interest from the community, I’d be happy to adjust the patch to better integrate with Vim's existing motion semantics.

Thanks for your time and attention!

Best regards,
Iago Jacob
iagojaco...@gmail.com

--
--
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- You received this message because you are subscribed to the Google Groups "vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/51a4146d-e17a-4e8b-9bab-4d7ff1a3342f%40gmail.com.
From 01795a414b064610044a0d634c0fc0ca01b3a54a Mon Sep 17 00:00:00 2001
From: Cristal-sys <iagojaco...@gmail.com>
Date: Sun, 8 Jun 2025 18:32:36 -0300
Subject: [PATCH] patched binsearch normal mode command

---
 runtime/doc/index.txt  |  3 +-
 runtime/doc/motion.txt | 12 ++++++++
 src/normal.c           | 70 ++++++++++++++++++++++++++++++++++++++++++
 src/nv_cmds.h          |  2 +-
 4 files changed, 85 insertions(+), 2 deletions(-)

diff --git a/runtime/doc/index.txt b/runtime/doc/index.txt
index 00a09ae14..26f9c32aa 100644
--- a/runtime/doc/index.txt
+++ b/runtime/doc/index.txt
@@ -223,7 +223,8 @@ tag		char	      note action in Normal mode	~
 |CTRL-N|	CTRL-N		1  same as "j"
 |CTRL-O|	CTRL-O		1  go to N older entry in jump list
 |CTRL-P|	CTRL-P		1  same as "k"
-		CTRL-Q		   not used, or used for terminal control flow
+|CTRL-Q|	CTRL-Q		   used for terminal control flow, or enter
+				   binary search cursormovement, see |binsearch|
 |CTRL-R|	CTRL-R		2  redo changes which were undone with 'u'
 		CTRL-S		   not used, or used for terminal control flow
 |CTRL-T|	CTRL-T		   jump to N older Tag in tag list
diff --git a/runtime/doc/motion.txt b/runtime/doc/motion.txt
index 726d15dde..0ba3eabc0 100644
--- a/runtime/doc/motion.txt
+++ b/runtime/doc/motion.txt
@@ -381,6 +381,18 @@ gg			Goto line [count], default first line, on the first
 			option in 'statusline'.
 			{not available when compiled without the
 			|+byte_offset| feature}
+			
+							*CTRL-Q* *binsearch*
+CTRL-Q
+			Enter binary search cursor movement. When CTRL-Q is
+			pressed, vim sets an upper and a lower bound that are
+			top line and the bottom line of the screen,
+			respectively, and whenever CTRL-K (CTRL-J) is pressed,
+			vim moves the cursor |linewise| until half-way between  			the current line and the top (bottom) line, thus moving
+			up (down), and updating the lower (upper) bound to 
+			equal the previous cursor position. If CTRL-Q is 
+			pressed again, the upper and lower bounds are
+			restarted.
 
 These commands move to the specified line.  They stop when reaching the first
 or the last line.  The first two commands put the cursor in the same column
diff --git a/src/normal.c b/src/normal.c
index c631651a0..607fd3c2d 100644
--- a/src/normal.c
+++ b/src/normal.c
@@ -122,6 +122,7 @@ static void	nv_nbcmd(cmdarg_T *cap);
 static void	nv_drop(cmdarg_T *cap);
 #endif
 static void	nv_cursorhold(cmdarg_T *cap);
+static void	nv_binsearch(cmdarg_T *cap);
 
 // Declare nv_cmds[].
 #define DO_DECLARE_NVCMD
@@ -7571,3 +7572,72 @@ nv_cursorhold(cmdarg_T *cap)
     did_cursorhold = TRUE;
     cap->retval |= CA_COMMAND_BUSY;	// don't call edit() now
 }
+/*
+ * Move cursor by Binary Search. Ctrl_J to move down and Ctrl_K to move up.
+ */
+    static void
+nv_binsearch(cmdarg_T *cap)
+{
+    static linenr_T upper_bound=0,
+		    lower_bound=0,
+		    center_bound=0;	//upper bound and lower bound refer to the line number, i.e. the lower bound
+					//appears at the top of the window (since line number grows downward).
+    int c = 0;
+    
+    static int binsearch_mode = FALSE;
+    
+    if(!binsearch_mode)			//initialize upper and lower bounds and center_bound
+    {
+	binsearch_mode = TRUE;
+        center_bound = curwin->w_cursor.lnum;
+	
+	validate_botline();	    // make sure curwin->w_botline is valid
+	upper_bound = curwin->w_botline;
+	lower_bound = curwin->w_topline;
+    }
+
+    cap->oap->motion_type = MLINE;
+
+    while(TRUE)
+    {
+    c = vgetc();
+
+    //if the user typed Ctrl_Q again, they want to re-start the binary search.
+    if(c == Ctrl_Q)
+    {
+	binsearch_mode = FALSE;
+	vungetc(c);
+	return;
+
+    }
+    if(c==Ctrl_J)
+    {
+
+    lower_bound=center_bound;
+    center_bound = (center_bound + upper_bound)/2;
+    cursor_down(center_bound - lower_bound, TRUE);	//use cursor_down to move instead of any other method
+    vungetc(Ctrl_Q);					//like, say, curwin->w_cursor.lnum =center_line, just in case.
+    return;						//we return after every cursor movement 
+							//to update whatever visual efect may be triggered by
+							//cursor movement, like visual mode.
+							//unget Ctrl_Q to make sure we will come back to
+							//this function after we go back to main and update visual effects.
+    }
+
+    if(c==Ctrl_K)
+    {
+
+    upper_bound=center_bound;
+    center_bound = (center_bound + lower_bound)/2;
+    cursor_up(upper_bound - center_bound, TRUE);
+    vungetc(Ctrl_Q);
+    return;
+    
+    }
+    break;
+
+    }
+    vungetc(c);	//unget c to resume the command that interrupted the binary search
+
+    binsearch_mode = FALSE;
+}
diff --git a/src/nv_cmds.h b/src/nv_cmds.h
index aa68b686b..bd0e32a48 100644
--- a/src/nv_cmds.h
+++ b/src/nv_cmds.h
@@ -126,7 +126,7 @@ static const int nv_cmds[] =
     NVCMD(Ctrl_N,	nv_down,	NV_STS,			FALSE),
     NVCMD(Ctrl_O,	nv_ctrlo,	0,			0),
     NVCMD(Ctrl_P,	nv_up,		NV_STS,			FALSE),
-    NVCMD(Ctrl_Q,	nv_visual,	0,			FALSE),
+    NVCMD(Ctrl_Q,	nv_binsearch,	0,			0),
     NVCMD(Ctrl_R,	nv_redo_or_register, 0,			0),
     NVCMD(Ctrl_S,	nv_ignore,	0,			0),
     NVCMD(Ctrl_T,	nv_tagpop,	NV_NCW,			0),
-- 
2.34.1

Raspunde prin e-mail lui