This file is part of GNU Emacs.
-GNU Emacs is free software; you can redistribute it and/or modify
+GNU Emacs is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 3, or (at your option)
-any later version.
+the Free Software Foundation, either version 3 of the License, or
+(at your option) any later version.
GNU Emacs is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
-along with GNU Emacs; see the file COPYING. If not, write to
-the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-Boston, MA 02110-1301, USA. */
+along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
#include <config.h>
#include <stdio.h>
#include "buffer.h"
#include "character.h"
#include "category.h"
+#include "composite.h"
#include "indent.h"
#include "keyboard.h"
#include "frame.h"
#include "termopts.h"
#include "disptab.h"
#include "intervals.h"
+#include "dispextern.h"
#include "region-cache.h"
/* Indentation can insert tabs if this is non-zero;
return pos;
}
\f
-/* If a composition starts at POS/POS_BYTE and it doesn't stride over
- POINT, set *LEN / *LEN_BYTE to the character and byte lengths, *WIDTH
- to the width, and return 1. Otherwise, return 0. */
-
-static int
-check_composition (pos, pos_byte, point, len, len_byte, width)
- int pos, pos_byte, point;
- int *len, *len_byte, *width;
-{
- Lisp_Object prop;
- EMACS_INT start, end;
- int id;
-
- if (! find_composition (pos, -1, &start, &end, &prop, Qnil)
- || pos != start || point < end
- || !COMPOSITION_VALID_P (start, end, prop))
- return 0;
- if ((id = get_composition_id (pos, pos_byte, end - pos, prop, Qnil)) < 0)
- return 0;
-
- *len = COMPOSITION_LENGTH (prop);
- *len_byte = CHAR_TO_BYTE (end) - pos_byte;
- *width = composition_table[id]->width;
- return 1;
-}
-\f
/* Set variables WIDTH and BYTES for a multibyte sequence starting at P.
DP is a display table or NULL.
register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
register struct Lisp_Char_Table *dp = buffer_display_table ();
int multibyte = !NILP (current_buffer->enable_multibyte_characters);
+ struct composition_it cmp_it;
+ Lisp_Object window;
+ struct window *w;
/* Start the scan at the beginning of this line with column number 0. */
register EMACS_INT col = 0, prev_col = 0;
next_boundary = scan;
}
+ window = Fget_buffer_window (Fcurrent_buffer (), Qnil);
+ w = ! NILP (window) ? XWINDOW (window) : NULL;
+
if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
+ bzero (&cmp_it, sizeof cmp_it);
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, scan, scan_byte, end, Qnil);
/* Scan forward to the target position. */
while (scan < end)
break;
prev_col = col;
- { /* Check composition sequence. */
- int len, len_byte, width;
-
- if (check_composition (scan, scan_byte, end,
- &len, &len_byte, &width))
- {
- scan += len;
- scan_byte += len_byte;
- if (scan <= end)
- col += width;
- continue;
- }
- }
-
{ /* Check display property. */
EMACS_INT end;
int width = check_display_width (scan, col, &end);
}
}
+ /* Check composition sequence. */
+ if (cmp_it.id >= 0
+ || (scan == cmp_it.stop_pos
+ && composition_reseat_it (&cmp_it, scan, scan_byte, end,
+ w, NULL, Qnil)))
+ composition_update_it (&cmp_it, scan, scan_byte, Qnil);
+ if (cmp_it.id >= 0)
+ {
+ scan += cmp_it.nchars;
+ scan_byte += cmp_it.nbytes;
+ if (scan <= end)
+ col += cmp_it.width;
+ if (cmp_it.to == cmp_it.nglyphs)
+ {
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, scan, scan_byte, end,
+ Qnil);
+ }
+ else
+ cmp_it.from = cmp_it.to;
+ continue;
+ }
+
c = FETCH_BYTE (scan_byte);
/* See if there is a display table and it relates
EMACS_INT prev_tab_offset; /* Previous tab offset. */
EMACS_INT continuation_glyph_width;
+ struct composition_it cmp_it;
+
XSETBUFFER (buffer, current_buffer);
XSETWINDOW (window, win);
pos_byte = prev_pos_byte = CHAR_TO_BYTE (from);
contin_hpos = 0;
prev_tab_offset = tab_offset;
+ bzero (&cmp_it, sizeof cmp_it);
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, pos, pos_byte, to, Qnil);
+
while (1)
{
while (pos == next_boundary)
if (hpos > width)
{
- if (hscroll
- || (truncate_partial_width_windows
- && ((width + continuation_glyph_width)
- < FRAME_COLS (XFRAME (WINDOW_FRAME (win)))))
+ int total_width = width + continuation_glyph_width;
+ int truncate = 0;
+
+ if (!NILP (Vtruncate_partial_width_windows)
+ && (total_width < FRAME_COLS (XFRAME (WINDOW_FRAME (win)))))
+ {
+ if (INTEGERP (Vtruncate_partial_width_windows))
+ truncate
+ = total_width < XFASTINT (Vtruncate_partial_width_windows);
+ else
+ truncate = 1;
+ }
+
+ if (hscroll || truncate
|| !NILP (current_buffer->truncate_lines))
{
/* Truncating: skip to newline, unless we are already past
EMACS_INT i, n;
Lisp_Object charvec;
- c = FETCH_BYTE (pos_byte);
-
/* Check composition sequence. */
- {
- int len, len_byte, width;
-
- if (check_composition (pos, pos_byte, to, &len, &len_byte, &width))
- {
- pos += len;
- pos_byte += len_byte;
- hpos += width;
- continue;
- }
- }
+ if (cmp_it.id >= 0
+ || (pos == cmp_it.stop_pos
+ && composition_reseat_it (&cmp_it, pos, pos_byte, to, win,
+ NULL, Qnil)))
+ composition_update_it (&cmp_it, pos, pos_byte, Qnil);
+ if (cmp_it.id >= 0)
+ {
+ pos += cmp_it.nchars;
+ pos_byte += cmp_it.nbytes;
+ hpos += cmp_it.width;
+ if (cmp_it.to == cmp_it.nglyphs)
+ {
+ cmp_it.id = -1;
+ composition_compute_stop_pos (&cmp_it, pos, pos_byte, to,
+ Qnil);
+ }
+ else
+ cmp_it.from = cmp_it.to;
+ continue;
+ }
+ c = FETCH_BYTE (pos_byte);
pos++, pos_byte++;
/* Perhaps add some info to the width_run_cache. */
parameters such as width, horizontal scrolling, and so on.
The default is to use the selected window's parameters.
+LINES can optionally take the form (COLS . LINES), in which case
+the motion will not stop at the start of a screen line but on
+its column COLS (if such exists on that line, that is).
+
`vertical-motion' always uses the current buffer,
regardless of which buffer is displayed in WINDOW.
This is consistent with other cursor motion functions
struct window *w;
Lisp_Object old_buffer;
struct gcpro gcpro1;
+ Lisp_Object lcols = Qnil;
+ double cols;
+
+ /* Allow LINES to be of the form (HPOS . VPOS) aka (COLUMNS . LINES). */
+ if (CONSP (lines) && (NUMBERP (XCAR (lines))))
+ {
+ lcols = XCAR (lines);
+ cols = INTEGERP (lcols) ? (double) XINT (lcols) : XFLOAT_DATA (lcols);
+ lines = XCDR (lines);
+ }
CHECK_NUMBER (lines);
if (! NILP (window))
}
else
{
- int it_start;
- int oselective;
- int it_overshoot_expected;
+ int it_start, oselective, first_x, it_overshoot_expected;
SET_TEXT_POS (pt, PT, PT_BYTE);
start_display (&it, w, pt);
-
- /* Scan from the start of the line containing PT. If we don't
- do this, we start moving with IT->current_x == 0, while PT is
- really at some x > 0. The effect is, in continuation lines, that
- we end up with the iterator placed at where it thinks X is 0,
- while the end position is really at some X > 0, the same X that
- PT had. */
+ first_x = it.first_visible_x;
it_start = IT_CHARPOS (it);
- /* We expect the call to move_it_to, further down, to overshoot
- if the starting point is on an image, stretch glyph,
- composition, or Lisp string. We won't need to backtrack in
- this situation, except for one corner case: when the Lisp
- string contains a newline. */
- if (it.method == GET_FROM_STRING)
+ /* See comments below for why we calculate this. */
+ if (XINT (lines) > 0)
{
- const char *s = SDATA (it.string);
- const char *e = s + SBYTES (it.string);
-
- while (s < e && *s != '\n')
- ++s;
-
- /* If there is no newline in the string, we need to check
- whether there is a newline immediately after the string
- in move_it_to below. This may happen if there is an
- overlay with an after-string just before the newline. */
- it_overshoot_expected = (s == e) ? -1 : 0;
+ if (it.cmp_it.id >= 0)
+ it_overshoot_expected = 1;
+ else if (it.method == GET_FROM_STRING)
+ {
+ const char *s = SDATA (it.string);
+ const char *e = s + SBYTES (it.string);
+ while (s < e && *s != '\n')
+ ++s;
+ it_overshoot_expected = (s == e) ? -1 : 0;
+ }
+ else
+ it_overshoot_expected = (it.method == GET_FROM_IMAGE
+ || it.method == GET_FROM_STRETCH);
}
- else
- it_overshoot_expected = (it.method == GET_FROM_IMAGE
- || it.method == GET_FROM_STRETCH
- || it.method == GET_FROM_COMPOSITION);
+ /* Scan from the start of the line containing PT. If we don't
+ do this, we start moving with IT->current_x == 0, while PT is
+ really at some x > 0. */
reseat_at_previous_visible_line_start (&it);
it.current_x = it.hpos = 0;
/* Temporarily disable selective display so we don't move too far */
move_it_to (&it, PT, -1, -1, -1, MOVE_TO_POS);
it.selective = oselective;
- /* Move back if we got too far. This may happen if
- truncate-lines is on and PT is beyond right margin.
- Don't go back if the overshoot is expected (see above). */
- if (IT_CHARPOS (it) > it_start && XINT (lines) > 0
- && (!it_overshoot_expected
- || (it_overshoot_expected < 0
- && it.method == GET_FROM_BUFFER
- && it.c == '\n')))
- move_it_by_lines (&it, -1, 0);
-
- it.vpos = 0;
- /* Do this even if LINES is 0, so that we move back
- to the beginning of the current line as we ought. */
- if (XINT (lines) >= 0 || IT_CHARPOS (it) > 0)
- move_it_by_lines (&it, XINT (lines), 0);
+ if (XINT (lines) <= 0)
+ {
+ it.vpos = 0;
+ /* Do this even if LINES is 0, so that we move back to the
+ beginning of the current line as we ought. */
+ if (XINT (lines) == 0 || IT_CHARPOS (it) > 0)
+ move_it_by_lines (&it, XINT (lines), 0);
+ }
+ else
+ {
+ if (IT_CHARPOS (it) > it_start)
+ {
+ /* IT may move too far if truncate-lines is on and PT
+ lies beyond the right margin. In that case,
+ backtrack unless the starting point is on an image,
+ stretch glyph, composition, or Lisp string. */
+ if (!it_overshoot_expected
+ /* Also, backtrack if the Lisp string contains no
+ newline, but there is a newline right after it.
+ In this case, IT overshoots if there is an
+ after-string just before the newline. */
+ || (it_overshoot_expected < 0
+ && it.method == GET_FROM_BUFFER
+ && it.c == '\n'))
+ move_it_by_lines (&it, -1, 0);
+ it.vpos = 0;
+ move_it_by_lines (&it, XINT (lines), 0);
+ }
+ else
+ {
+ /* Otherwise, we are at the first row occupied by PT,
+ which might span multiple screen lines (e.g., if it's
+ on a multi-line display string). We want to start
+ from the last line that it occupies. */
+ if (it_start < ZV)
+ {
+ while (IT_CHARPOS (it) <= it_start)
+ {
+ it.vpos = 0;
+ move_it_by_lines (&it, 1, 0);
+ }
+ if (XINT (lines) > 1)
+ move_it_by_lines (&it, XINT (lines) - 1, 0);
+ }
+ else
+ {
+ it.vpos = 0;
+ move_it_by_lines (&it, XINT (lines), 0);
+ }
+ }
+ }
+
+ /* Move to the goal column, if one was specified. */
+ if (!NILP (lcols))
+ {
+ /* If the window was originally hscrolled, move forward by
+ the hscrolled amount first. */
+ if (first_x > 0)
+ {
+ move_it_in_display_line (&it, ZV, first_x, MOVE_TO_X);
+ it.current_x = 0;
+ }
+ move_it_in_display_line
+ (&it, ZV,
+ (int)(cols * FRAME_COLUMN_WIDTH (XFRAME (w->frame)) + 0.5),
+ MOVE_TO_X);
+ }
SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
}