Hello,

Here's a patch to change the default keybindings for the arrow keys in
the `info' program to be more browser-like.

Here's the rationale:  The browsing metaphor is something that new and
existing users of `info' understand implicitly.  By accepting this
patch, we're trying to make `info' easier to use for new and existing
users.

Here's the implemented behaviour:
Left Arrow means "go back".  Going "back" means go back to the last
page that was viewed (as opposed to the previous page in the book I'm
reading.)
Right Arrow means "follow this link".  Going forward means following a
link[0] (as opposed to flipping the page).
Up and Down Arrows make the cursor move to the next link.  If no link
is available, the page is scrolled up or down.

Please note that this patch will not currently function correctly if
use_regex is set to 1 in info/session.c.  Set it to 0 if you wish to
test this patch with CVS head.

0. I am using the term `link' to mean an Info "menu item", or "cross
reference" (xref).

regards,

Ben
diff -uNrd texinfo.orig/info/info.c texinfo/info/info.c
--- texinfo.orig/info/info.c	2008-01-03 11:06:48.000000000 -0500
+++ texinfo/info/info.c	2008-01-03 10:51:42.000000000 -0500
@@ -662,6 +662,7 @@
 const char *msg_no_menu_node;
 const char *msg_no_foot_node;
 const char *msg_no_xref_node;
+const char *msg_no_xref_node_here;
 const char *msg_no_pointer;
 const char *msg_unknown_command;
 const char *msg_term_too_dumb;
@@ -682,6 +683,7 @@
   msg_no_menu_node     = _("No menu in this node.");
   msg_no_foot_node     = _("No footnotes in this node.");
   msg_no_xref_node     = _("No cross references in this node.");
+  msg_no_xref_node_here = _("No cross reference to follow.");
   msg_no_pointer       = _("No `%s' pointer for this node.");
   msg_unknown_command  = _("Unknown Info command `%c'; try `?' for help.");
   msg_term_too_dumb    = _("Terminal type `%s' is not smart enough to run Info.");
diff -uNrd texinfo.orig/info/info.h texinfo/info/info.h
--- texinfo.orig/info/info.h	2007-07-01 17:20:30.000000000 -0400
+++ texinfo/info/info.h	2007-12-31 20:26:57.000000000 -0500
@@ -138,6 +138,7 @@
 extern const char *msg_no_menu_node;
 extern const char *msg_no_foot_node;
 extern const char *msg_no_xref_node;
+extern const char *msg_no_xref_node_here;
 extern const char *msg_no_pointer;
 extern const char *msg_unknown_command;
 extern const char *msg_term_too_dumb;
diff -uNrd texinfo.orig/info/infomap.c texinfo/info/infomap.c
--- texinfo.orig/info/infomap.c	2007-12-17 14:12:11.000000000 -0500
+++ texinfo/info/infomap.c	2007-12-29 20:31:05.000000000 -0500
@@ -974,18 +974,18 @@
 
         SK_ESCAPE, SK_PAGE_UP, NUL,             A_info_scroll_backward_page_only,
         SK_ESCAPE, SK_PAGE_DOWN, NUL,           A_info_scroll_forward_page_only,
-        SK_ESCAPE, SK_UP_ARROW, NUL,            A_info_prev_line,
-        '\033', 'O', 'A', NUL,                  A_info_prev_line,
-        '\033', '[', 'A', NUL,                  A_info_prev_line,
-        SK_ESCAPE, SK_DOWN_ARROW, NUL,          A_info_next_line,
-        '\033', 'O', 'B', NUL,                  A_info_next_line,
-        '\033', '[', 'B', NUL,                  A_info_next_line,
-        SK_ESCAPE, SK_RIGHT_ARROW, NUL,         A_info_forward_char,
-        '\033', 'O', 'C', NUL,                  A_info_forward_char,
-        '\033', '[', 'C', NUL,                  A_info_forward_char,
-        SK_ESCAPE, SK_LEFT_ARROW, NUL,          A_info_backward_char,
-        '\033', 'O', 'D', NUL,                  A_info_backward_char,
-        '\033', '[', 'D', NUL,                  A_info_backward_char,
+        SK_ESCAPE, SK_UP_ARROW, NUL,            A_info_move_to_prev_xref_or_scroll,
+        '\033', 'O', 'A', NUL,                  A_info_move_to_prev_xref_or_scroll,
+        '\033', '[', 'A', NUL,                  A_info_move_to_prev_xref_or_scroll,
+        SK_ESCAPE, SK_DOWN_ARROW, NUL,          A_info_move_to_next_xref_or_scroll,
+        '\033', 'O', 'B', NUL,                  A_info_move_to_next_xref_or_scroll,
+        '\033', '[', 'B', NUL,                  A_info_move_to_next_xref_or_scroll,
+        SK_ESCAPE, SK_RIGHT_ARROW, NUL,         A_info_select_reference_this_line,
+        '\033', 'O', 'C', NUL,                  A_info_select_reference_this_line,
+        '\033', '[', 'C', NUL,                  A_info_select_reference_this_line,
+        SK_ESCAPE, SK_LEFT_ARROW, NUL,          A_info_history_node,
+        '\033', 'O', 'D', NUL,                  A_info_history_node,
+        '\033', '[', 'D', NUL,                  A_info_history_node,
         SK_ESCAPE, SK_HOME, NUL,                A_info_beginning_of_node,
         SK_ESCAPE, SK_END, NUL,                 A_info_end_of_node,
         SK_ESCAPE, SK_DELETE, NUL,              A_info_scroll_backward,
diff -uNrd texinfo.orig/info/session.c texinfo/info/session.c
--- texinfo.orig/info/session.c	2007-12-17 14:12:11.000000000 -0500
+++ texinfo/info/session.c	2008-01-03 11:00:29.000000000 -0500
@@ -1241,16 +1241,16 @@
     }
 }
 
-static void _scroll_forward(WINDOW *window, int count,
+static int _scroll_forward(WINDOW *window, int count,
     unsigned char key, int behaviour);
-static void _scroll_backward(WINDOW *window, int count,
+static int _scroll_backward(WINDOW *window, int count,
     unsigned char key, int behaviour);
 
-static void
+static int
 _scroll_forward(WINDOW *window, int count, unsigned char key, int behaviour)
 {
   if (count < 0)
-    _scroll_backward (window, -count, key, behaviour);
+    return _scroll_backward (window, -count, key, behaviour);
   else
     {
       int desired_top;
@@ -1268,10 +1268,7 @@
           /* If there are no more lines to scroll here, error, or get
              another node, depending on BEHAVIOUR. */
           if (desired_top > window->line_count)
-            {
-              forward_move_node_structure (window, behaviour);
-              return;
-            }
+	    return forward_move_node_structure (window, behaviour);
         }
       else
         desired_top = window->pagetop + count;
@@ -1284,13 +1281,14 @@
       else
         set_window_pagetop (window, desired_top);
     }
+  return 0;
 }
 
-static void
+static int
 _scroll_backward(WINDOW *window, int count, unsigned char key, int behaviour)
 {
   if (count < 0)
-    _scroll_forward (window, -count, key, behaviour);
+    return _scroll_forward (window, -count, key, behaviour);
   else
     {
       int desired_top;
@@ -1305,10 +1303,7 @@
           desired_top = window->pagetop - (window->height - 2);
 
           if ((desired_top < 0) && (window->pagetop == 0))
-            {
-              backward_move_node_structure (window, behaviour);
-              return;
-            }
+	    return backward_move_node_structure (window, behaviour);
         }
       else
         desired_top = window->pagetop - count;
@@ -1318,6 +1313,7 @@
 
       set_window_pagetop (window, desired_top);
     }
+  return 0;
 }
 
 /* Show the next screen of WINDOW's node. */
@@ -2126,15 +2122,22 @@
 
    This is not the same logic as in info.el.  Info-get-token prefers
    searching backwards to searching forwards, and has a hardwired search
-   limit of 200 chars (in Emacs 21.2).  */
+   limit of 200 chars (in Emacs 21.2).  
+   
+   When MAX_POS is not -1, the xrefs must appear before MAX_POS. */
 
 static REFERENCE **
-nearest_xref (REFERENCE **xref_list, long int pos)
+nearest_xref (REFERENCE **xref_list, long int pos, int max_pos)
 {
   int this_xref;
   int nearest = -1;
   long best_delta = -1;
+  int max_delta = 0;
   
+  /* Throw out deltas after the given point. */
+  if (max_pos != -1)
+    max_delta = max_pos - pos;
+
   for (this_xref = 0; xref_list[this_xref]; this_xref++)
     {
       long delta;
@@ -2151,6 +2154,9 @@
       delta = MIN (labs (pos - (xref->start - strlen (INFO_XREF_LABEL))),
                    labs (pos - xref->end));
       
+      if (max_pos != -1 && delta > max_delta)
+	continue;
+
       /* It's the <= instead of < that makes us choose the forward xref
          of POS if two are equidistant.  Of course, because of all the
          punctuation surrounding xrefs, it's not necessarily obvious
@@ -2180,22 +2186,32 @@
    reference found on the current line, and select that node.  The
    reading is done with completion.  BUILDER is the function used
    to build the list of references.  ASK_P is non-zero if the user
-   should be prompted, or zero to select the default item. */
+   should be prompted, or zero to select the default item. 
+   When FOLLOW_XREF_ON_THIS_LINE_ONLY is set to 1, a special behaviour
+   to xrefs is applied:  We don't follow the nearest xref, we follow
+   the xref on this line, or not at all. */
 static void
 info_menu_or_ref_item (WINDOW *window, int count,
-    unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p)
+    unsigned char key, REFERENCE **(*builder) (NODE *node), int ask_p,
+    int follow_xref_on_this_line_only)
 {
   char *line;
   REFERENCE *entry;
   REFERENCE *defentry = NULL;
   REFERENCE **menu = (*builder) (window->node);
+  int follow_xref = 0;
 
   if (!menu)
     {
       if (builder == info_menu_of_node)
         info_error ((char *) msg_no_menu_node, NULL, NULL);
-      else
+      else if (!follow_xref_on_this_line_only)
+	/* We check FOLLOW_XREF_ON_THIS_LINE_ONLY to inhibit a message
+	   that doesn't make sense when we try to follow an xref
+	   when there isn't one on the line. */
         info_error ((char *) msg_no_xref_node, NULL, NULL);
+      else
+        info_error ((char *) msg_no_xref_node_here, NULL, NULL);
       return;
     }
 
@@ -2232,7 +2248,23 @@
               refs = manpage_xrefs_in_binding (window->node, &binding);
             else
 #endif /* HANDLE_MAN_PAGES */
-              refs = nearest_xref (menu, window->point);
+	      {
+		if (follow_xref_on_this_line_only)
+		  {
+		    int point_line = window_line_of_point (window);
+		    if (point_line != -1)
+		      {
+			int max_pos;
+			int old_point = window->point;
+			info_end_of_line (window, count, key);
+			max_pos = window->point;
+			window->point = old_point;
+			refs = nearest_xref (menu, window->point, max_pos);
+		      }
+		  }
+		else
+		  refs = nearest_xref (menu, window->point, -1);
+	      }
           }
 
         if (refs && refs[0])
@@ -2444,7 +2476,7 @@
    and select that item. */
 DECLARE_INFO_COMMAND (info_menu_item, _("Read a menu item and select its node"))
 {
-  info_menu_or_ref_item (window, count, key, info_menu_of_node, 1);
+  info_menu_or_ref_item (window, count, key, info_menu_of_node, 1, 0);
 }
 
 /* Read a line (with completion) which is the name of a reference to
@@ -2452,7 +2484,7 @@
 DECLARE_INFO_COMMAND
   (info_xref_item, _("Read a footnote or cross reference and select its node"))
 {
-  info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1);
+  info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 1, 0);
 }
 
 /* Position the cursor at the start of this node's menu. */
@@ -4474,6 +4506,181 @@
   return 0;
 }
 
+/* Move to the next or previous cross reference in this node, or scroll the
+   page if the next/previous link is too far. */
+static int
+info_move_to_xref_or_scroll (WINDOW *window, int count, unsigned char key, int dir)
+{
+  int err;
+  long firstmenu, firstxref;
+  long nextmenu, nextxref;
+  long placement = -1;
+  long start = 0;
+  NODE *node = window->node;
+  int old_point;
+  int old_pagetop;
+  int xref_point;
+  int xref_pagetop;
+
+  if (dir < 0)
+    start = node->nodelen;
+
+  /* This search is only allowed to fail if there is no menu or cross
+     reference in the current node.  Otherwise, the first menu or xref
+     found is moved to. */
+
+  firstmenu = info_search_in_node
+    (INFO_MENU_ENTRY_LABEL, node, start, (WINDOW *)NULL, dir, 0);
+
+  /* FIRSTMENU may point directly to the line defining the menu.  Skip that
+     and go directly to the first item. */
+
+  if (firstmenu != -1)
+    {
+      char *text = node->contents + firstmenu;
+
+      if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
+        firstmenu = info_search_in_node
+          (INFO_MENU_ENTRY_LABEL, node, firstmenu + dir, 
+	   (WINDOW *)NULL, dir, 0);
+    }
+
+  firstxref =
+    info_search_in_node (INFO_XREF_LABEL, node, start, (WINDOW *)NULL, dir, 0);
+
+#if defined (HANDLE_MAN_PAGES)
+  if ((firstxref == -1) && (node->flags & N_IsManPage))
+    {
+      firstxref = locate_manpage_xref (node, start, dir);
+    }
+#endif /* HANDLE_MAN_PAGES */
+
+  if (firstmenu == -1 && firstxref == -1)
+    {
+      /* Just scroll up or down because we can't see a menu or an xref. */
+      if (dir < 0)
+	{
+	  err = _scroll_backward (window, count, key, IS_PageOnly);
+	  if (err)
+	    info_beginning_of_node (window, 1, key);
+	}
+      else
+	{
+	  err = _scroll_forward (window, count, key, IS_PageOnly);
+	  if (err)
+	    info_end_of_node (window, 1, key);
+	}
+      return 0;
+    }
+
+  /* There is at least one cross reference or menu entry in this node.
+     Try hard to find the next available one. */
+
+  nextmenu = info_search_in_node
+    (INFO_MENU_ENTRY_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
+
+  nextxref = info_search_in_node
+    (INFO_XREF_LABEL, node, window->point + dir, (WINDOW *)NULL, dir, 0);
+
+#if defined (HANDLE_MAN_PAGES)
+  if ((nextxref == -1) && (node->flags & N_IsManPage) && (firstxref != -1))
+    nextxref = locate_manpage_xref (node, window->point + dir, dir);
+#endif /* HANDLE_MAN_PAGES */
+
+  /* Ignore "Menu:" as a menu item. */
+  if (nextmenu != -1)
+    {
+      char *text = node->contents + nextmenu;
+
+      if (strncmp (text, INFO_MENU_LABEL, strlen (INFO_MENU_LABEL)) == 0)
+        nextmenu = info_search_in_node
+          (INFO_MENU_ENTRY_LABEL, node, nextmenu + dir, (WINDOW *)NULL, dir, 0);
+    }
+
+  /* If there is both a next menu entry, and a next xref entry, choose the
+     one which occurs first. */
+  if (nextmenu != -1 && nextxref != -1)
+    {
+      if (((dir == 1) && (nextmenu < nextxref)) ||
+          ((dir == -1) && (nextmenu > nextxref)))
+        placement = nextmenu + 1;
+      else
+        placement = nextxref;
+    }
+  else if (nextmenu != -1)
+    placement = nextmenu + 1;
+  else if (nextxref != -1)
+    placement = nextxref;
+
+  if (placement == -1)
+    {
+      if (cursor_movement_scrolls_p)
+        return 1;
+      else
+	{
+	  /* There isn't another menu or xref, so we just scroll. */
+	  if (dir < 0)
+	    {
+	      err = _scroll_backward (window, count, key, IS_PageOnly);
+	      if (err)
+		info_beginning_of_node (window, 1, key);
+	    }
+	  else
+	    {
+	      err = _scroll_forward (window, count, key, IS_PageOnly);
+	      if (err)
+		info_end_of_node (window, 1, key);
+	    }
+	  return 0;
+	}
+    }
+
+      
+  old_point = window->point;
+  old_pagetop = window->pagetop;
+
+  window->point = placement;
+  window_adjust_pagetop (window);
+  
+  xref_point = window->point;
+  xref_pagetop = window->pagetop;
+
+  window->point = old_point;
+  window->pagetop = old_pagetop;
+
+
+  /* Is the menu or xref point on this page? */
+  if (window_point_visible (window, xref_point) == 0)
+    {
+      /* No, now we scroll. */
+      if (dir < 0)
+	{
+	  err = _scroll_backward (window, count, key, IS_PageOnly);
+	  if (err)
+	    info_beginning_of_node (window, 1, key);
+	}
+      else
+	{
+	  err = _scroll_forward (window, count, key, IS_PageOnly);
+	  if (err)
+	    info_end_of_node (window, 1, key);
+	}
+  
+      /* Is the xref on this new page?  Go there. */
+      if (window_point_visible (window, xref_point))
+	window->point = xref_point;
+
+    }
+  else
+    {
+      window->point = xref_point;
+      window->pagetop = xref_pagetop;
+    }
+
+  window->flags |= W_UpdateWindow;
+  return 0;
+}
+
 DECLARE_INFO_COMMAND (info_move_to_prev_xref,
                       _("Move to the previous cross reference"))
 {
@@ -4513,6 +4720,44 @@
     }
 }
 
+DECLARE_INFO_COMMAND (info_move_to_next_xref_or_scroll,
+                      _("Move to the next cross reference or scroll forward"))
+{
+  if (count < 0)
+    info_move_to_next_xref_or_scroll (window, -count, key);
+  else
+    {
+      int err;
+
+      err = info_move_to_xref_or_scroll (window, count, key, 1);
+      if (err)
+	{
+	  info_error_was_printed = 0;
+	  if (!forward_move_node_structure (window, info_scroll_behaviour))
+	    move_to_new_line (0, 0, window);
+	}
+    }
+}
+
+DECLARE_INFO_COMMAND (info_move_to_prev_xref_or_scroll,
+                      _("Move to the previous cross reference or scroll backward"))
+{
+  if (count < 0)
+    info_move_to_prev_xref_or_scroll (window, -count, key);
+  else
+    {
+      int err;
+
+      err = info_move_to_xref_or_scroll (window, count, key, -1);
+      if (err)
+	{
+	  info_error_was_printed = 0;
+	  if (!backward_move_node_structure (window, info_scroll_behaviour))
+	    move_to_new_line (0, 0, window);
+	}
+    }
+}
+
 /* Select the menu item or reference that appears on this line. */
 DECLARE_INFO_COMMAND (info_select_reference_this_line,
                       _("Select reference or menu item appearing on this line"))
@@ -4526,10 +4771,11 @@
 
   /* If this line contains a menu item, select that one. */
   if (strncmp ("* ", line, 2) == 0)
-    info_menu_or_ref_item (window, count, key, info_menu_of_node, 0);
+    info_menu_or_ref_item (window, count, key, info_menu_of_node, 0, 0);
   else
-    info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0);
+    info_menu_or_ref_item (window, count, key, info_xrefs_of_node, 0, 1);
 }
+
 
 /* **************************************************************** */
 /*                                                                  */
diff -uNrd texinfo.orig/info/session.h texinfo/info/session.h
--- texinfo.orig/info/session.h	2007-12-17 14:12:11.000000000 -0500
+++ texinfo/info/session.h	2007-12-31 17:53:17.000000000 -0500
@@ -129,6 +129,8 @@
 extern void info_end_of_node (WINDOW *window, int count, unsigned char key);
 extern void info_move_to_prev_xref (WINDOW *window, int count, unsigned char key);
 extern void info_move_to_next_xref (WINDOW *window, int count, unsigned char key);
+extern void info_move_to_next_xref_or_scroll (WINDOW *window, int count, unsigned char key);
+extern void info_move_to_prev_xref_or_scroll (WINDOW *window, int count, unsigned char key);
 
 /* Scrolling text within a window. */
 extern void info_scroll_forward (WINDOW *window, int count, unsigned char key);
diff -uNrd texinfo.orig/info/window.c texinfo/info/window.c
--- texinfo.orig/info/window.c	2007-10-19 14:43:20.000000000 -0400
+++ texinfo/info/window.c	2007-12-31 19:42:47.000000000 -0500
@@ -1002,6 +1002,35 @@
   return (i - 1);
 }
 
+/* Is the POINT visible on the WINDOW without scrolling? 
+   Returns 0 if not, 1 if it is. */
+int
+window_point_visible (WINDOW *window, long point)
+{
+  register int idx, start = window->pagetop;
+  long window_start_point = 0;
+  long window_end_point = 0;
+  int len;
+
+  idx = start;
+  window_start_point = window->line_starts[idx] - window->node->contents;
+  if (start + window->height > window->line_count - 1)
+    idx = window->line_count - 1;
+  else
+    idx = start + window->height;
+  window_end_point = window->line_starts[idx] - window->node->contents;
+  len = window->node->nodelen;
+
+  for (; (window_end_point < len) && 
+       (window->node->contents[window_end_point] != '\n');
+       window_end_point++);
+
+  if (point >= window_start_point &&  point <= window_end_point)
+    return 1;
+  else
+    return 0;
+}
+
 /* Get and return the goal column for this window. */
 int
 window_get_goal_column (WINDOW *window)
diff -uNrd texinfo.orig/info/window.h texinfo/info/window.h
--- texinfo.orig/info/window.h	2007-07-01 17:20:31.000000000 -0400
+++ texinfo/info/window.h	2007-12-29 20:31:05.000000000 -0500
@@ -223,6 +223,9 @@
 /* Return the index of the line containing point. */
 extern int window_line_of_point (WINDOW *window);
 
+/* Return whether or not POINT is currently visible in WINDOW. */
+extern int window_point_visible (WINDOW *window, long point);
+
 /* Get and return the goal column for this window. */
 extern int window_get_goal_column (WINDOW *window);
 

Reply via email to