X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/88bc8332eb14bcc4780fd3fe3dd4de2205c31dbf..8833692b29ba11c34413d6793cf6d222ccdd930b:/src/indent.c diff --git a/src/indent.c b/src/indent.c index 589aeb9c00..0ef8903501 100644 --- a/src/indent.c +++ b/src/indent.c @@ -1,13 +1,13 @@ /* 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 @@ -26,10 +26,8 @@ along with GNU Emacs. If not, see . */ #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" @@ -485,7 +483,9 @@ check_display_width (ptrdiff_t pos, ptrdiff_t col, ptrdiff_t *endpos) : 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) @@ -504,6 +504,18 @@ check_display_width (ptrdiff_t pos, ptrdiff_t col, ptrdiff_t *endpos) *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; } } @@ -1928,7 +1940,22 @@ vmotion (register ptrdiff_t from, register ptrdiff_t from_byte, -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. @@ -1951,28 +1978,32 @@ is). If the line is scrolled horizontally, COLS is interpreted 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); } @@ -1980,7 +2011,6 @@ whether or not it is currently displayed in some window. */) 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. */ @@ -2006,6 +2036,16 @@ whether or not it is currently displayed in some window. */) 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); @@ -2042,11 +2082,19 @@ whether or not it is currently displayed in some window. */) 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 @@ -2082,6 +2130,15 @@ whether or not it is currently displayed in some window. */) && 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 @@ -2090,35 +2147,46 @@ whether or not it is currently displayed in some window. */) 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 @@ -2137,21 +2205,42 @@ whether or not it is currently displayed in some window. */) if (nlines > 1) move_it_by_lines (&it, min (PTRDIFF_MAX, nlines - 1)); } - else + else /* it_start = ZV */ { it.vpos = 0; move_it_by_lines (&it, min (PTRDIFF_MAX, nlines)); + /* We could have some display or overlay string at ZV, + in which case it.vpos will be nonzero now, while + actually we didn't move vertically at all. */ + if (IT_CHARPOS (it) == CHARPOS (pt) && CHARPOS (pt) == it_start) + it.vpos = 0; } } /* 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)); @@ -2165,7 +2254,7 @@ whether or not it is currently displayed in some window. */) old_charpos, old_bytepos); } - RETURN_UNGCPRO (make_number (it.vpos)); + return make_number (it.vpos); }