emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: The unwarranted scrolling assumption


From: Eli Zaretskii
Subject: Re: The unwarranted scrolling assumption
Date: Sun, 20 Jun 2010 21:17:10 +0300

> Date: Sun, 20 Jun 2010 18:24:18 +0300
> From: Eli Zaretskii <address@hidden>
> Cc: address@hidden, address@hidden, address@hidden
> 
> It's a bit silly to do that, when we have a dVCS, but here you go
> anyway:

Here's an updated version:

----------------------------------------------------------------------
static int
try_scrolling (window, just_this_one_p, scroll_conservatively,
               scroll_step, temp_scroll_step, last_line_misfit)
     Lisp_Object window;
     int just_this_one_p;
     EMACS_INT scroll_conservatively, scroll_step;
     int temp_scroll_step;
     int last_line_misfit;
{
  struct window *w = XWINDOW (window);
  struct frame *f = XFRAME (w->frame);
  struct text_pos pos, startp;
  struct it it;
  int this_scroll_margin, scroll_max, rc, height;
  int dy = 0, amount_to_scroll = 0, scroll_down_p = 0;
  int extra_scroll_margin_lines = last_line_misfit ? 1 : 0;
  Lisp_Object aggressive;
  int scroll_limit = INT_MAX / FRAME_LINE_HEIGHT (f);

#if GLYPH_DEBUG
  debug_method_add (w, "try_scrolling");
#endif

  SET_TEXT_POS_FROM_MARKER (startp, w->start);

  /* Compute scroll margin height in pixels.  We scroll when point is
     within this distance from the top or bottom of the window.  */
  if (scroll_margin > 0)
    this_scroll_margin = min (scroll_margin, WINDOW_TOTAL_LINES (w) / 4)
      * FRAME_LINE_HEIGHT (f);
  else
    this_scroll_margin = 0;

  /* Force scroll_conservatively to have a reasonable value, to avoid
     overflow while computing how much to scroll.  Note that the user
     can supply scroll-conservatively equal to `most-positive-fixnum',
     which can be larger than INT_MAX.  */
  if (scroll_conservatively > scroll_limit)
    {
      scroll_conservatively = scroll_limit;
      scroll_max = INT_MAX;
    }
  else if (scroll_step || scroll_conservatively || temp_scroll_step)
    /* Compute how much we should try to scroll maximally to bring
       point into view.  */
    scroll_max = (max (scroll_step,
                       max (scroll_conservatively, temp_scroll_step))
                  * FRAME_LINE_HEIGHT (f));
  else if (NUMBERP (current_buffer->scroll_down_aggressively)
           || NUMBERP (current_buffer->scroll_up_aggressively))
    /* We're trying to scroll because of aggressive scrolling but no
       scroll_step is set.  Choose an arbitrary one.  */
    scroll_max = 10 * FRAME_LINE_HEIGHT (f);
  else
    scroll_max = 0;

 too_near_end:

  /* Decide whether to scroll down.  */
  if (PT > CHARPOS (startp))
    {
      int scroll_margin_y;

      /* Compute the pixel ypos of the scroll margin, then move it to
         either that ypos or PT, whichever comes first.  */
      start_display (&it, w, startp);
      scroll_margin_y = it.last_visible_y - this_scroll_margin
        - FRAME_LINE_HEIGHT (f) * extra_scroll_margin_lines;
      move_it_to (&it, PT, -1, scroll_margin_y - 1, -1,
                  (MOVE_TO_POS | MOVE_TO_Y));

      if (PT > CHARPOS (it.current.pos))
        {
          int y0 = line_bottom_y (&it);
          /* Compute how many pixels below window bottom to stop searching
             for PT.  This avoids costly search for PT that is far away if
             the user limited scrolling by a small number of lines, but
             always finds PT if scroll_conservatively is set to a large
             number, such as most-positive-fixnum.  */
          int slack = max (scroll_max, 10 * FRAME_LINE_HEIGHT (f));
          int y_to_move =
            slack >= INT_MAX - it.last_visible_y
            ? INT_MAX
            : it.last_visible_y + slack;

          /* Compute the distance from the scroll margin to PT or to
             the scroll limit, whichever comes first.  This should
             include the height of the cursor line, to make that line
             fully visible.  */
          move_it_to (&it, PT, -1, y_to_move,
                      -1, MOVE_TO_POS | MOVE_TO_Y);
          dy = line_bottom_y (&it) - y0;

          if (dy > scroll_max)
            return SCROLLING_FAILED;

          scroll_down_p = 1;
        }
    }

  if (scroll_down_p)
    {
      /* Point is in or below the bottom scroll margin, so move the
         window start down.  If scrolling conservatively, move it just
         enough down to make point visible.  If scroll_step is set,
         move it down by scroll_step.  */
      if (scroll_conservatively)
        amount_to_scroll
          = min (max (dy, FRAME_LINE_HEIGHT (f)),
                 FRAME_LINE_HEIGHT (f) * scroll_conservatively);
      else if (scroll_step || temp_scroll_step)
        amount_to_scroll = scroll_max;
      else
        {
          aggressive = current_buffer->scroll_up_aggressively;
          height = WINDOW_BOX_TEXT_HEIGHT (w);
          if (NUMBERP (aggressive))
            {
              double float_amount = XFLOATINT (aggressive) * height;
              amount_to_scroll = float_amount;
              if (amount_to_scroll == 0 && float_amount > 0)
                amount_to_scroll = 1;
            }
        }

      if (amount_to_scroll <= 0)
        return SCROLLING_FAILED;

      start_display (&it, w, startp);
      if (scroll_max < INT_MAX)
        move_it_vertically (&it, amount_to_scroll);
      else
        {
          /* Extra precision for users who set scroll-conservatively
             to most-positive-fixnum: make sure the amount we scroll
             the window start is never less than amount_to_scroll,
             which was computed as distance from window bottom to
             point.  This matters when lines at window top and lines
             below window bottom have different height.  */
          struct it it1 = it;
          /* We use a temporary it1 because line_bottom_y can modify
             its argument, if it moves one line down; see there.  */
          int start_y = line_bottom_y (&it1);

          do {
            move_it_by_lines (&it, 1, 1);
            it1 = it;
          } while (line_bottom_y (&it1) - start_y < amount_to_scroll);
        }

      /* If STARTP is unchanged, move it down another screen line.  */
      if (CHARPOS (it.current.pos) == CHARPOS (startp))
        move_it_by_lines (&it, 1, 1);
      startp = it.current.pos;
    }
  else
    {
      struct text_pos scroll_margin_pos = startp;

      /* See if point is inside the scroll margin at the top of the
         window.  */
      if (this_scroll_margin)
        {
          start_display (&it, w, startp);
          move_it_vertically (&it, this_scroll_margin);
          scroll_margin_pos = it.current.pos;
        }

      if (PT < CHARPOS (scroll_margin_pos))
        {
          /* Point is in the scroll margin at the top of the window or
             above what is displayed in the window.  */
          int y0;

          /* Compute the vertical distance from PT to the scroll
             margin position.  Give up if distance is greater than
             scroll_max.  */
          SET_TEXT_POS (pos, PT, PT_BYTE);
          start_display (&it, w, pos);
          y0 = it.current_y;
          move_it_to (&it, CHARPOS (scroll_margin_pos), 0,
                      it.last_visible_y, -1,
                      MOVE_TO_POS | MOVE_TO_X | MOVE_TO_Y);
          dy = it.current_y - y0;
          if (dy > scroll_max)
            return SCROLLING_FAILED;

          /* Compute new window start.  */
          start_display (&it, w, startp);

          if (scroll_conservatively)
            amount_to_scroll
              = max (dy, FRAME_LINE_HEIGHT (f) * max (scroll_step, 
temp_scroll_step));
          else if (scroll_step || temp_scroll_step)
            amount_to_scroll = scroll_max;
          else
            {
              aggressive = current_buffer->scroll_down_aggressively;
              height = WINDOW_BOX_TEXT_HEIGHT (w);
              if (NUMBERP (aggressive))
                {
                  double float_amount = XFLOATINT (aggressive) * height;
                  amount_to_scroll = float_amount;
                  if (amount_to_scroll == 0 && float_amount > 0)
                    amount_to_scroll = 1;
                }
            }

          if (amount_to_scroll <= 0)
            return SCROLLING_FAILED;

          move_it_vertically_backward (&it, amount_to_scroll);
          startp = it.current.pos;
        }
    }

  /* Run window scroll functions.  */
  startp = run_window_scroll_functions (window, startp);

  /* Display the window.  Give up if new fonts are loaded, or if point
     doesn't appear.  */
  if (!try_window (window, startp, 0))
    rc = SCROLLING_NEED_LARGER_MATRICES;
  else if (w->cursor.vpos < 0)
    {
      clear_glyph_matrix (w->desired_matrix);
      rc = SCROLLING_FAILED;
    }
  else
    {
      /* Maybe forget recorded base line for line number display.  */
      if (!just_this_one_p
          || current_buffer->clip_changed
          || BEG_UNCHANGED < CHARPOS (startp))
        w->base_line_number = Qnil;

      /* If cursor ends up on a partially visible line,
         treat that as being off the bottom of the screen.  */
      if (! cursor_row_fully_visible_p (w, extra_scroll_margin_lines <= 1, 0))
        {
          clear_glyph_matrix (w->desired_matrix);
          ++extra_scroll_margin_lines;
          goto too_near_end;
        }
      rc = SCROLLING_SUCCESS;
    }

  return rc;
}



reply via email to

[Prev in Thread] Current Thread [Next in Thread]