emacs-devel
[Top][All Lists]
Advanced

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

Problems with move_it_in_display_line_to X when tabs exist.


From: Keith David Bershatsky
Subject: Problems with move_it_in_display_line_to X when tabs exist.
Date: Tue, 28 Nov 2017 22:12:35 -0800

I have reached a road block calculating X and HPOS when dealing with tabs while 
working on feature requests 17684 (crosshairs) and 22873 (multiple fake 
cursors).

DETAILS:  Native line numbers are visible.  No overlays are present.  No 
special text properties are present.

GOAL:  The goal is to move IT from it.first_visible_x to it.last_visible_x on 
the line containing POINT and place fake cursors on each tab, adjustable 
tab-width, and/or character.  As I move IT over the line containing POINT, I 
want to reliably obtain X and HPOS.  This works well if there are no tabs.  
And, it works well if there are just a couple of tabs with a tab-width of 2.  
At some point, however, it stops working when there are more than just a couple 
of tabs on the same line, especially when they are mixed somewhere within the 
middle of the line.  I am using draw_window_cursor to create multiple fake 
cursors (with the underscore shape) on the entire current line, to create a 
horizontal ruler spanning the window-body-width.

I can see that X and/or HPOS are wrong when tabs are present on the current 
line because I get superimposed letters and double of the same word slightly 
off to the left and/or the right of where the word should be.

HYPOTHESIS:  I would venture to say that what is displayed on screen does _not_ 
coincide with what IT reports when running move_it_in_display_line_to.

IDEAS:  Erase the entire current line and redraw it so that what is displayed 
on screen matches exactly what IT reports when running 
move_it_in_display_line_to.

Any additional ideas or thoughts on how to deal with tabs in this scenario 
would be greatly appreciated.


SNIPPET USED TO CREATE HORIZONTAL LINE:

  while (true)
    {
      flavor = (it.c != '\t') ? Qmc_glyph : Qmc_glyph_tab;
      if (hscl)
        {
          relative_x = it.current_x - (hscl_first_hpos * frame_char_width);
          hpos = it.hpos - hscl_first_hpos;
        }
        else
          {
            relative_x = it.current_x - first_x;
            hpos = it.hpos;
          }
      if (header_line_height > 0)
        vpos = it.vpos + 1;
        else
          vpos = it.vpos;
      bool lpw_reached_p = ((hscl
                             && it.current_x >= hscl_first_x + lnum_pixel_width)
                            || (!hscl
                                && it.current_x >= first_x + lnum_pixel_width));
      bool final_loop_p = (ITERATOR_AT_END_OF_LINE_P (&it)
                           || FETCH_BYTE (IT_BYTEPOS (it)) == '\n'
                           || rc == MOVE_POS_MATCH_OR_ZV);
      bool tab_invisible_p = (it.c == '\t');
      bool tab_visible_p = (it.c != '\t'
                            && FETCH_BYTE (IT_BYTEPOS (it)) == '\t');
      bool real_fake_cursor_p = (opoint_x == relative_x
                                 && opoint_y == it.current_y
                                 && opoint_hpos == hpos
                                 && opoint_vpos == vpos);
      if (!real_fake_cursor_p
          && lpw_reached_p
          && !tab_invisible_p)
        draw_fake_cursor (w, relative_x, it.current_y, hpos, vpos,
                          HBAR_CURSOR, cursor_width,
                          foreground, color_vector (w, it.face_id),
                          flavor, IT_CHARPOS (it), &result);
      if (!real_fake_cursor_p
          && lpw_reached_p
          && tab_invisible_p)
        {
          int i, count = it.pixel_width / frame_char_width;
          for (i = 0; i < count; i++)
            {
              draw_fake_cursor (w, relative_x, it.current_y, hpos, vpos,
                                HBAR_CURSOR, cursor_width,
                                foreground, color_vector (w, it.face_id),
                                flavor, IT_CHARPOS (it), &result);
              relative_x = relative_x + frame_char_width;
            }
        }
      if (final_loop_p)
        break;
      rc = mc_move_it_in_display_line (w, &it, it.current_x + it.pixel_width);
      if (rc == MOVE_LINE_CONTINUED)
        break;
      relative_x = (hscl)
                   ? it.current_x - (hscl_first_hpos * frame_char_width)
                   : it.current_x - first_x;
      if (relative_x + frame_char_width >= text_area_width)
        break;
    }


int
mc_move_it_in_display_line (struct window *w, struct it *it, int target_x)
{
  if (IT_CHARPOS (*it) == ZV)
    return MOVE_POS_MATCH_OR_ZV;
  struct it saved_it;
  void *saved_data = bidi_shelve_cache ();
  enum move_it_result rc = MOVE_X_REACHED;
  int new_x, prev_x;
  /* When `auto-hscroll-mode' is set to `current-line` and we are horizontal 
scrolling
  a long line that approaches or exceeds an `it.current.x` of approximately 
1000, `rc`
  will erroneously return early with a MOVE_LINE_TRUNCATED indicator without 
pushing
  forwards until IT reaches the target_x.  As a workaround, ignore 
MOVE_LINE_TRUNCATED. */
  while (it->current_x + it->pixel_width <= target_x
         && (rc == MOVE_X_REACHED
             || rc == MOVE_LINE_TRUNCATED
             || (it->line_wrap == WORD_WRAP
                 && rc == MOVE_POS_MATCH_OR_ZV)))
    {
      SAVE_IT (saved_it, *it, saved_data);
      new_x = it->current_x + it->pixel_width;
      if (new_x == it->current_x)
        new_x++;
      rc = move_it_in_display_line_to (it, ZV, new_x, MOVE_TO_POS | MOVE_TO_X);
      if (ITERATOR_AT_END_OF_LINE_P (it)
          || FETCH_BYTE (IT_BYTEPOS (*it)) == '\n'
          /* #28936:  `move_it_in_display_line_to' returns MOVE_POS_MATCH_OR_ZV
          before reaching ZV when the latter is at the end of the line AND 
`word-wrap'
          is non-nil:  abcdefg[ZV].  The workaround is to add an extra check 
using
          IT_CHARPOS and comparing it to ZV. */
          || (rc == MOVE_POS_MATCH_OR_ZV
              && IT_CHARPOS (*it) == ZV))
        break;
    }
  /* When word-wrap is on, TO_X may lie past the end of a wrapped line.
  Then it->current is the character on the next line, so backtrack to the
  space before the wrap point.  */
  if (it->line_wrap == WORD_WRAP
      && rc == MOVE_LINE_CONTINUED)
    {
      prev_x = max (it->current_x - 1, 0);
      RESTORE_IT (it, &saved_it, saved_data);
      move_it_in_display_line_to (it, -1, prev_x, MOVE_TO_X);
    }
  bidi_unshelve_cache (saved_data, true);
  /* Workaround for bug #28936 -- correct the erroneous MOVE_POS_MATCH_OR_ZV. */
  if (it->current_x == target_x
      && rc == MOVE_POS_MATCH_OR_ZV
      && IT_CHARPOS (*it) != ZV)
    rc = MOVE_X_REACHED;
  return rc;
}



reply via email to

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