[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;
}
- Problems with move_it_in_display_line_to X when tabs exist.,
Keith David Bershatsky <=