/* Indentation functions.
- Copyright (C) 1985-1988, 1993-1995, 1998, 2000-2015 Free Software
+ Copyright (C) 1985-1988, 1993-1995, 1998, 2000-2016 Free Software
Foundation, Inc.
This file is part of GNU Emacs.
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 of the License, 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
#include "category.h"
#include "composite.h"
#include "indent.h"
-#include "keyboard.h"
#include "frame.h"
#include "window.h"
-#include "termchar.h"
#include "disptab.h"
#include "intervals.h"
#include "dispextern.h"
/* With a display table entry, C is displayed as is, and
not displayed as \NNN or as ^N. If C is a single-byte
character, it takes one column. If C is multi-byte in
- an unibyte buffer, it's translated to unibyte, so it
+ a unibyte buffer, it's translated to unibyte, so it
also takes one column. */
++col;
else
: MOST_POSITIVE_FIXNUM);
if ((prop = Fplist_get (plist, QCwidth),
- RANGED_INTEGERP (0, prop, INT_MAX)))
+ RANGED_INTEGERP (0, prop, INT_MAX))
+ || (prop = Fplist_get (plist, QCrelative_width),
+ RANGED_INTEGERP (0, prop, INT_MAX)))
width = XINT (prop);
else if (FLOATP (prop) && 0 <= XFLOAT_DATA (prop)
&& XFLOAT_DATA (prop) <= INT_MAX)
*endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
else
get_property_and_range (pos, Qdisplay, &val, &start, endpos, Qnil);
+
+ /* For :relative-width, we need to multiply by the column
+ width of the character at POS, if it is greater than 1. */
+ if (!NILP (Fplist_get (plist, QCrelative_width))
+ && !NILP (BVAR (current_buffer, enable_multibyte_characters)))
+ {
+ int b, wd;
+ unsigned char *p = BYTE_POS_ADDR (CHAR_TO_BYTE (pos));
+
+ MULTIBYTE_BYTES_WIDTH (p, buffer_display_table (), b, wd);
+ width *= wd;
+ }
return width;
}
}
column += tab_width - column % tab_width;
break;
default:
- if (ASCII_BYTE_P (p[-1])
+ if (ASCII_CHAR_P (p[-1])
|| NILP (BVAR (current_buffer, enable_multibyte_characters)))
return column;
{
-1, hscroll, 0, w);
}
-DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
+/* In window W (derived from WINDOW), return x coordinate for column
+ COL (derived from COLUMN). */
+static int
+window_column_x (struct window *w, Lisp_Object window,
+ double col, Lisp_Object column)
+{
+ double x = col * FRAME_COLUMN_WIDTH (XFRAME (w->frame)) + 0.5;
+
+ /* FIXME: Should this be limited to W's dimensions? */
+ if (! (INT_MIN <= x && x <= INT_MAX))
+ args_out_of_range (window, column);
+
+ return x;
+}
+
+DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 3, 0,
doc: /* Move point to start of the screen line LINES lines down.
If LINES is negative, this means moving up.
visually, i.e., as addition to the columns of text beyond the left
edge of the window.
+The optional third argument CUR-COL specifies the horizontal
+window-relative coordinate of point, in units of frame's canonical
+character width, where the function is invoked. If this argument is
+omitted or nil, the function will determine the point coordinate by
+going back to the beginning of the line.
+
`vertical-motion' always uses the current buffer,
regardless of which buffer is displayed in WINDOW.
This is consistent with other cursor motion functions
and makes it possible to use `vertical-motion' in any buffer,
whether or not it is currently displayed in some window. */)
- (Lisp_Object lines, Lisp_Object window)
+ (Lisp_Object lines, Lisp_Object window, Lisp_Object cur_col)
{
struct it it;
struct text_pos pt;
struct window *w;
Lisp_Object old_buffer;
EMACS_INT old_charpos IF_LINT (= 0), old_bytepos IF_LINT (= 0);
- struct gcpro gcpro1;
- Lisp_Object lcols = Qnil;
- double cols IF_LINT (= 0);
+ Lisp_Object lcols;
void *itdata = NULL;
/* Allow LINES to be of the form (HPOS . VPOS) aka (COLUMNS . LINES). */
- if (CONSP (lines) && (NUMBERP (XCAR (lines))))
+ bool lcols_given = CONSP (lines);
+ if (lcols_given)
{
lcols = XCAR (lines);
- cols = INTEGERP (lcols) ? (double) XINT (lcols) : XFLOAT_DATA (lcols);
lines = XCDR (lines);
}
w = decode_live_window (window);
old_buffer = Qnil;
- GCPRO1 (old_buffer);
if (XBUFFER (w->contents) != current_buffer)
{
/* Set the window's buffer temporarily to the current buffer. */
bool disp_string_at_start_p = 0;
ptrdiff_t nlines = XINT (lines);
int vpos_init = 0;
+ double start_col IF_LINT (= 0);
+ int start_x IF_LINT (= 0);
+ int to_x = -1;
+
+ bool start_x_given = !NILP (cur_col);
+ if (start_x_given)
+ {
+ start_col = extract_float (cur_col);
+ start_x = window_column_x (w, window, start_col, cur_col);
+ }
itdata = bidi_shelve_cache ();
SET_TEXT_POS (pt, PT, PT_BYTE);
it_overshoot_count =
!(it.method == GET_FROM_IMAGE || it.method == GET_FROM_STRETCH);
- /* 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;
+ if (start_x_given)
+ {
+ it.hpos = start_col;
+ it.current_x = start_x;
+ }
+ else
+ {
+ /* 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;
+ }
if (IT_CHARPOS (it) != PT)
/* We used to temporarily disable selective display here; the
comment said this is "so we don't move too far" (2005-01-19
&& it.method == GET_FROM_BUFFER
&& it.c == '\n')
it_overshoot_count = 1;
+ else if (it_overshoot_count == 1 && it.vpos == 0
+ && it.current_x < it.last_visible_x)
+ {
+ /* If we came to the same screen line as the one where
+ we started, we didn't overshoot the line, and won't
+ need to backtrack after all. This happens, for
+ example, when PT is in the middle of a composition. */
+ it_overshoot_count = 0;
+ }
else if (disp_string_at_start_p && it.vpos > 0)
{
/* This is the case of a display string that spans
screen lines we need to backtrack. */
it_overshoot_count = it.vpos;
}
+ /* We will overshoot if lines are truncated and point lies
+ beyond the right margin of the window. */
+ if (it.line_wrap == TRUNCATE && it.current_x >= it.last_visible_x
+ && it_overshoot_count == 0)
+ it_overshoot_count = 1;
if (it_overshoot_count > 0)
move_it_by_lines (&it, -it_overshoot_count);
overshoot_handled = 1;
}
else if (IT_CHARPOS (it) == PT - 1
- && FETCH_BYTE (PT - 1) == '\n'
- && nlines < 0)
+ && FETCH_BYTE (PT_BYTE - 1) == '\n'
+ && nlines <= 0)
{
/* The position we started from was covered by a display
property, so we moved to position before the string, and
- backed up one line, because the character at PT - 1 is a
- newline. So we need one less line to go up. */
+ backed up one line, because the character at PT - 1 is
+ a newline. So we need one less line to go up (or exactly
+ one line to go down if nlines == 0). */
nlines++;
/* But we still need to record that one line, in order to
return the correct value to the caller. */
vpos_init = -1;
+
+ overshoot_handled = 1;
}
+ if (lcols_given)
+ to_x = window_column_x (w, window, extract_float (lcols), lcols);
if (nlines <= 0)
{
it.vpos = vpos_init;
/* Do this even if LINES is 0, so that we move back to the
beginning of the current line as we ought. */
- if (nlines == 0 || IT_CHARPOS (it) > 0)
+ if ((nlines < 0 && IT_CHARPOS (it) > 0)
+ || (nlines == 0 && !(start_x_given && start_x <= to_x)))
move_it_by_lines (&it, max (PTRDIFF_MIN, nlines));
}
else if (overshoot_handled)
{
- it.vpos = 0;
+ it.vpos = vpos_init;
move_it_by_lines (&it, min (PTRDIFF_MAX, nlines));
}
else
/* Move to the goal column, if one was specified. If the window
was originally hscrolled, the goal column is interpreted as
an addition to the hscroll amount. */
- if (!NILP (lcols))
+ if (lcols_given)
{
- int to_x = (int)(cols * FRAME_COLUMN_WIDTH (XFRAME (w->frame)) + 0.5);
-
move_it_in_display_line (&it, ZV, first_x + to_x, MOVE_TO_X);
+ /* If we find ourselves in the middle of an overlay string
+ which includes a newline after current string position,
+ we need to move by lines until we get out of the string,
+ and then reposition point at the requested X coordinate;
+ if we don't, the cursor will be placed just after the
+ string, which might not be the requested column. */
+ if (nlines > 0 && it.area == TEXT_AREA)
+ {
+ while (it.method == GET_FROM_STRING
+ && !it.string_from_display_prop_p
+ && memchr (SSDATA (it.string) + IT_STRING_BYTEPOS (it),
+ '\n',
+ SBYTES (it.string) - IT_STRING_BYTEPOS (it)))
+ {
+ move_it_by_lines (&it, 1);
+ move_it_in_display_line (&it, ZV, first_x + to_x, MOVE_TO_X);
+ }
+ }
}
SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
old_charpos, old_bytepos);
}
- RETURN_UNGCPRO (make_number (it.vpos));
+ return make_number (it.vpos);
}