changeset 2303:00e3cc81c1be

(decode_mode_spec): Handle `%l'. (display_count_lines): New function. (redisplay_window): Update base_line_number and base_line_pos fields. Always update mode line if it's an integer.
author Richard M. Stallman <rms@gnu.org>
date Sun, 21 Mar 1993 23:04:18 +0000
parents 560f7fcb759c
children 480abddc8bdd
files src/xdisp.c
diffstat 1 files changed, 159 insertions(+), 6 deletions(-) [+]
line wrap: on
line diff
--- a/src/xdisp.c	Sun Mar 21 22:58:10 1993 +0000
+++ b/src/xdisp.c	Sun Mar 21 23:04:18 1993 +0000
@@ -129,6 +129,7 @@
 static char *decode_mode_spec ();
 static int display_string ();
 static void display_menu_bar ();
+static int display_count_lines ();
 
 /* Prompt to display in front of the minibuffer contents */
 char *minibuf_prompt;
@@ -168,6 +169,12 @@
    since last redisplay that finished */
 int windows_or_buffers_changed;
 
+/* Nonzero after display_mode_line if %l was used
+   and it displayed a line number.  */
+int line_number_displayed;
+
+/* Maximum buffer size for which to display line numbers.  */
+int line_number_display_limit;
 
 /* Specify m, a string, as a message in the minibuf.  If m is 0, clear out
    any existing message, and let the minibuffer text show through.  */
@@ -819,9 +826,11 @@
   startp = marker_position (w->start);
 
   /* Handle case where place to start displaying has been specified,
-     unless the specified location is outside the visible range.  */
+     unless the specified location is outside the accessible range.  */
   if (!NILP (w->force_start))
     {
+      /* Forget any recorded base line for line number display.  */
+      w->base_line_number = Qnil;
       w->update_mode_line = Qt;
       w->force_start = Qnil;
       XFASTINT (w->last_modified) = 0;
@@ -928,7 +937,12 @@
       /* If point has not moved off frame, accept the results */
       try_window (window, startp);
       if (cursor_vpos >= 0)
-	goto done;
+	{
+	  if (!just_this_one || clip_changed || beg_unchanged < startp)
+	    /* Forget any recorded base line for line number display.  */
+	    w->base_line_number = Qnil;
+	  goto done;
+	}
       else
 	cancel_my_columns (w);
     }
@@ -955,7 +969,12 @@
 	{
 	  try_window (window, pos.bufpos);
 	  if (cursor_vpos >= 0)
-	    goto done;
+	    {
+	      if (!just_this_one || clip_changed || beg_unchanged < startp)
+		/* Forget any recorded base line for line number display.  */
+		w->base_line_number = Qnil;
+	      goto done;
+	    }
 	  else
 	    cancel_my_columns (w);
 	}
@@ -965,6 +984,9 @@
   /* Finally, just choose place to start which centers point */
 
 recenter:
+  /* Forget any previously recorded base line for line number display.  */
+  w->base_line_number = Qnil;
+
   pos = *vmotion (point, - height / 2, width, hscroll, window);
   try_window (window, pos.bufpos);
 
@@ -973,12 +995,19 @@
     (startp == BEGV || FETCH_CHAR (startp - 1) == '\n') ? Qt : Qnil;
 
 done:
-  /* If window not full width, must redo its mode line
-     if the window to its side is being redone */
   if ((!NILP (w->update_mode_line)
-       || (!just_this_one && width < FRAME_WIDTH (f) - 1))
+       /* If window not full width, must redo its mode line
+	  if the window to its side is being redone */
+       || (!just_this_one && width < FRAME_WIDTH (f) - 1)
+       || INTEGERP (w->base_line_pos))
       && height != XFASTINT (w->height))
     display_mode_line (w);
+  if (! line_number_displayed
+      && ! BUFFERP (w->base_line_pos))
+    {
+      w->base_line_pos = Qnil;
+      w->base_line_number = Qnil;
+    }
 
   /* When we reach a frame's selected window, redo the frame's menu bar.  */
   if (!NILP (w->update_mode_line)
@@ -1889,6 +1918,8 @@
   register int right = XFASTINT (w->width) + left;
   register FRAME_PTR f = XFRAME (WINDOW_FRAME (w));
 
+  line_number_displayed = 0;
+
   get_display_line (f, vpos, left);
   display_mode_element (w, vpos, left, 0, right, right,
 			current_buffer->mode_line_format);
@@ -2181,6 +2212,93 @@
 #endif
       break;
 
+    case 'l':
+      {
+	int startpos = marker_position (w->start);
+	int line, linepos, topline;
+	int nlines, junk;
+	Lisp_Object tem;
+	int height = XFASTINT (w->height);
+
+	/* If we decided that this buffer isn't suitable for line numbers, 
+	   don't forget that too fast.  */
+	if (EQ (w->base_line_pos, w->buffer))
+	  return "??";
+
+	/* If the buffer is very big, don't waste time.  */
+	if (ZV - BEGV > line_number_display_limit)
+	  {
+	    w->base_line_pos = Qnil;
+	    w->base_line_number = Qnil;
+	    return "??";
+	  }
+
+	if (!NILP (w->base_line_number)
+	    && !NILP (w->base_line_pos)
+	    && XFASTINT (w->base_line_pos) <= marker_position (w->start))
+	  {
+	    line = XFASTINT (w->base_line_number);
+	    linepos = XFASTINT (w->base_line_pos);
+	  }
+	else
+	  {
+	    line = 1;
+	    linepos = BEGV;
+	  }
+
+	/* Count lines from base line to window start position.  */
+	nlines = display_count_lines (linepos, startpos, startpos, &junk);
+
+	topline = nlines + line;
+
+	/* Determine a new base line, if the old one is too close
+	   or too far away, or if we did not have one.
+	   "Too close" means it's plausible a scroll-down would
+	   go back past it.  */
+	if (startpos == BEGV)
+	  {
+	    XFASTINT (w->base_line_number) = topline;
+	    XFASTINT (w->base_line_pos) = BEGV;
+	  }
+	else if (nlines < height + 25 || nlines > height * 3 + 50
+		 || linepos == BEGV)
+	  {
+	    int limit = BEGV;
+	    int position;
+	    int distance = (height * 2 + 30) * 200;
+
+	    if (startpos - distance > limit)
+	      limit = startpos - distance;
+
+	    nlines = display_count_lines (startpos, limit,
+					  -(height * 2 + 30),
+					  &position);
+	    /* If we couldn't find the lines we wanted within 
+	       200 chars per line,
+	       give up on line numbers for this window.  */
+	    if (position == startpos - distance)
+	      {
+		w->base_line_pos = w->buffer;
+		w->base_line_number = Qnil;
+		return "??";
+	      }
+
+	    XFASTINT (w->base_line_number) = topline - nlines;
+	    XFASTINT (w->base_line_pos) = position;
+	  }
+
+	/* Now count lines from the start pos to point.  */
+	nlines = display_count_lines (startpos, PT, PT, &junk);
+
+	/* Record that we did display the line number.  */
+	line_number_displayed = 1;
+
+	/* Make the string to show.  */
+	sprintf (decode_mode_spec_buf, "%d", topline + nlines);
+	return decode_mode_spec_buf;
+      }
+      break;
+
     case 'm': 
       obj = current_buffer->mode_name;
       break;
@@ -2284,6 +2402,37 @@
   else
     return "";
 }
+
+/* Count up to N lines starting from FROM.
+   But don't go beyond LIMIT.
+   Return the number of lines thus found (always positive).
+   Store the position after what was found into *POS_PTR.  */
+
+static int
+display_count_lines (from, limit, n, pos_ptr)
+     int from, limit, n;
+     int *pos_ptr;
+{
+  int oldbegv = BEGV;
+  int oldzv = ZV;
+  int shortage = 0;
+  
+  if (limit < from)
+    BEGV = limit;
+  else
+    ZV = limit;
+
+  *pos_ptr = scan_buffer ('\n', from, n, &shortage);
+
+  ZV = oldzv;
+  BEGV = oldbegv;
+
+  if (n < 0)
+    /* When scanning backwards, scan_buffer stops *after* the last newline
+       it finds, but does count it.  Compensate for that.  */
+    return - n - shortage - (*pos_ptr != limit);
+  return n - shortage;
+}  
 
 /* Display STRING on one line of window W, starting at HPOS.
    Display at position VPOS.  Caller should have done get_display_line.
@@ -2462,6 +2611,10 @@
   DEFVAR_BOOL ("mode-line-inverse-video", &mode_line_inverse_video,
     "*Non-nil means use inverse video for the mode line.");
   mode_line_inverse_video = 1;
+
+  DEFVAR_INT ("line-number-display-limit", &line_number_display_limit,
+    "*Maximum buffer size for which line number should be displayed.");
+  line_number_display_limit = 1000000;
 }
 
 /* initialize the window system */