#include <config.h>
#include "lisp.h"
#include "buffer.h"
#include "charset.h"
#include "category.h"
#include "indent.h"
#include "keyboard.h"
#include "frame.h"
#include "window.h"
#include "termchar.h"
#include "termopts.h"
#include "disptab.h"
#include "intervals.h"
#include "region-cache.h"
int indent_tabs_mode;
#define min(a, b) ((a) < (b) ? (a) : (b))
#define max(a, b) ((a) > (b) ? (a) : (b))
#define CR 015
int last_known_column;
int last_known_column_point;
int last_known_column_modified;
static int current_column_1 P_ ((void));
static int position_indentation P_ ((int));
int current_column_bol_cache;
struct Lisp_Char_Table *
buffer_display_table ()
{
Lisp_Object thisbuf;
thisbuf = current_buffer->display_table;
if (DISP_TABLE_P (thisbuf))
return XCHAR_TABLE (thisbuf);
if (DISP_TABLE_P (Vstandard_display_table))
return XCHAR_TABLE (Vstandard_display_table);
return 0;
}
static int
character_width (c, dp)
int c;
struct Lisp_Char_Table *dp;
{
Lisp_Object elt;
if (dp && (elt = DISP_CHAR_VECTOR (dp, c), VECTORP (elt)))
return XVECTOR (elt)->size;
if (c == '\n' || c == '\t' || c == '\015')
return 0;
else if (c >= 040 && c < 0177)
return 1;
else
return 0;
}
int
disptab_matches_widthtab (disptab, widthtab)
struct Lisp_Char_Table *disptab;
struct Lisp_Vector *widthtab;
{
int i;
if (widthtab->size != 256)
abort ();
for (i = 0; i < 256; i++)
if (character_width (i, disptab)
!= XFASTINT (widthtab->contents[i]))
return 0;
return 1;
}
void
recompute_width_table (buf, disptab)
struct buffer *buf;
struct Lisp_Char_Table *disptab;
{
int i;
struct Lisp_Vector *widthtab;
if (!VECTORP (buf->width_table))
buf->width_table = Fmake_vector (make_number (256), make_number (0));
widthtab = XVECTOR (buf->width_table);
if (widthtab->size != 256)
abort ();
for (i = 0; i < 256; i++)
XSETFASTINT (widthtab->contents[i], character_width (i, disptab));
}
static void
width_run_cache_on_off ()
{
if (NILP (current_buffer->cache_long_line_scans)
|| !NILP (current_buffer->enable_multibyte_characters))
{
if (current_buffer->width_run_cache)
{
free_region_cache (current_buffer->width_run_cache);
current_buffer->width_run_cache = 0;
current_buffer->width_table = Qnil;
}
}
else
{
if (current_buffer->width_run_cache == 0)
{
current_buffer->width_run_cache = new_region_cache ();
recompute_width_table (current_buffer, buffer_display_table ());
}
}
}
int
skip_invisible (pos, next_boundary_p, to, window)
int pos;
int *next_boundary_p;
int to;
Lisp_Object window;
{
Lisp_Object prop, position, overlay_limit, proplimit;
Lisp_Object buffer;
int end;
XSETFASTINT (position, pos);
XSETBUFFER (buffer, current_buffer);
recenter_overlay_lists (current_buffer, pos);
overlay_limit = Fnext_overlay_change (position);
proplimit = Fnext_property_change (position, buffer, Qt);
if (XFASTINT (overlay_limit) < XFASTINT (proplimit))
proplimit = overlay_limit;
if (XFASTINT (proplimit) > pos + 100 || XFASTINT (proplimit) >= to)
*next_boundary_p = XFASTINT (proplimit);
else
{
XSETFASTINT (proplimit, min (pos + 100, to));
if (XFASTINT (overlay_limit) < XFASTINT (proplimit))
proplimit = overlay_limit;
end = XFASTINT (Fnext_single_property_change (position, Qinvisible,
buffer, proplimit));
#if 0
if (end == pos + 100
&& !NILP (current_buffer->enable_multibyte_characters)
&& end < ZV)
while (pos < end && !CHAR_HEAD_P (POS_ADDR (end)))
end--;
#endif
*next_boundary_p = end;
}
if (!NILP (window) && EQ (XWINDOW (window)->buffer, buffer))
prop = Fget_char_property (position, Qinvisible, window);
else
prop = Fget_char_property (position, Qinvisible, buffer);
if (TEXT_PROP_MEANS_INVISIBLE (prop))
return *next_boundary_p;
return pos;
}
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;
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;
}
#define MULTIBYTE_BYTES_WIDTH(p, dp) \
do { \
int c; \
\
wide_column = 0; \
c = STRING_CHAR_AND_LENGTH (p, MAX_MULTIBYTE_LENGTH, bytes); \
if (BYTES_BY_CHAR_HEAD (*p) != bytes) \
width = bytes * 4; \
else \
{ \
if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c))) \
width = XVECTOR (DISP_CHAR_VECTOR (dp, c))->size; \
else \
width = WIDTH_BY_CHAR_HEAD (*p); \
if (width > 1) \
wide_column = width; \
} \
} while (0)
DEFUN ("current-column", Fcurrent_column, Scurrent_column, 0, 0, 0,
"Return the horizontal position of point. Beginning of line is column 0.\n\
This is calculated by adding together the widths of all the displayed\n\
representations of the character between the start of the previous line\n\
and point. (eg control characters will have a width of 2 or 4, tabs\n\
will have a variable width)\n\
Ignores finite width of frame, which means that this function may return\n\
values greater than (frame-width).\n\
Whether the line is visible (if `selective-display' is t) has no effect;\n\
however, ^M is treated as end of line when `selective-display' is t.")
()
{
Lisp_Object temp;
XSETFASTINT (temp, current_column ());
return temp;
}
void
invalidate_current_column ()
{
last_known_column_point = 0;
}
int
current_column ()
{
register int col;
register unsigned char *ptr, *stop;
register int tab_seen;
int post_tab;
register int c;
register int tab_width = XINT (current_buffer->tab_width);
int ctl_arrow = !NILP (current_buffer->ctl_arrow);
register struct Lisp_Char_Table *dp = buffer_display_table ();
if (PT == last_known_column_point
&& MODIFF == last_known_column_modified)
return last_known_column;
if (BUF_INTERVALS (current_buffer)
|| !NILP (current_buffer->overlays_before)
|| !NILP (current_buffer->overlays_after)
|| Z != Z_BYTE)
return current_column_1 ();
ptr = BYTE_POS_ADDR (PT_BYTE - 1) + 1;
if (PT == BEGV)
stop = ptr;
else if (PT <= GPT || BEGV > GPT)
stop = BEGV_ADDR;
else
stop = GAP_END_ADDR;
if (tab_width <= 0 || tab_width > 1000)
tab_width = 8;
col = 0, tab_seen = 0, post_tab = 0;
while (1)
{
EMACS_INT i, n;
Lisp_Object charvec;
if (ptr == stop)
{
if (ptr == BEGV_ADDR)
break;
stop = BEGV_ADDR;
ptr = GPT_ADDR;
if (BEGV >= GPT)
break;
}
c = *--ptr;
if (dp && VECTORP (DISP_CHAR_VECTOR (dp, c)))
{
charvec = DISP_CHAR_VECTOR (dp, c);
n = ASIZE (charvec);
}
else
{
charvec = Qnil;
n = 1;
}
for (i = n - 1; i >= 0; --i)
{
if (VECTORP (charvec))
{
Lisp_Object entry = AREF (charvec, i);
if (INTEGERP (entry)
&& GLYPH_CHAR_VALID_P (XFASTINT (entry)))
c = FAST_GLYPH_CHAR (XFASTINT (entry));
else
c = ' ';
}
if (c >= 040 && c < 0177)
col++;
else if (c == '\n'
|| (c == '\r'
&& EQ (current_buffer->selective_display, Qt)))
{
ptr++;
goto start_of_line_found;
}
else if (c == '\t')
{
if (tab_seen)
col = ((col + tab_width) / tab_width) * tab_width;
post_tab += col;
col = 0;
tab_seen = 1;
}
else if (VECTORP (charvec))
++col;
else
col += (ctl_arrow && c < 0200) ? 2 : 4;
}
}
start_of_line_found:
if (tab_seen)
{
col = ((col + tab_width) / tab_width) * tab_width;
col += post_tab;
}
if (ptr == BEGV_ADDR)
current_column_bol_cache = BEGV;
else
current_column_bol_cache = BYTE_TO_CHAR (PTR_BYTE_POS (ptr));
last_known_column = col;
last_known_column_point = PT;
last_known_column_modified = MODIFF;
return col;
}
static int
current_column_1 ()
{
register int tab_width = XINT (current_buffer->tab_width);
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);
register int col = 0;
int scan, scan_byte;
int next_boundary, next_boundary_byte;
int opoint = PT, opoint_byte = PT_BYTE;
scan_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, 1);
current_column_bol_cache = PT;
scan = PT, scan_byte = PT_BYTE;
SET_PT_BOTH (opoint, opoint_byte);
next_boundary = scan;
next_boundary_byte = scan_byte;
if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
while (scan < opoint)
{
int c;
EMACS_INT i, n;
Lisp_Object charvec;
while (scan == next_boundary)
{
int old_scan = scan;
scan = skip_invisible (scan, &next_boundary, opoint, Qnil);
if (scan >= opoint)
goto endloop;
if (scan != old_scan)
scan_byte = CHAR_TO_BYTE (scan);
next_boundary_byte = CHAR_TO_BYTE (next_boundary);
}
{
int len, len_byte, width;
if (check_composition (scan, scan_byte, opoint,
&len, &len_byte, &width))
{
scan += len;
scan_byte += len_byte;
if (scan <= opoint)
col += width;
continue;
}
}
c = FETCH_BYTE (scan_byte);
if (dp != 0
&& ! (multibyte && BASE_LEADING_CODE_P (c))
&& VECTORP (DISP_CHAR_VECTOR (dp, c)))
{
charvec = DISP_CHAR_VECTOR (dp, c);
n = ASIZE (charvec);
}
else
{
charvec = Qnil;
n = 1;
}
for (i = n - 1; i >= 0; --i)
{
if (VECTORP (charvec))
{
Lisp_Object entry = AREF (charvec, i);
if (INTEGERP (entry)
&& GLYPH_CHAR_VALID_P (XFASTINT (entry)))
c = FAST_GLYPH_CHAR (XFASTINT (entry));
else
c = ' ';
}
if (c == '\n')
goto endloop;
if (c == '\r' && EQ (current_buffer->selective_display, Qt))
goto endloop;
scan++;
scan_byte++;
if (c == '\t')
{
int prev_col = col;
col += tab_width;
col = col / tab_width * tab_width;
}
else if (multibyte && BASE_LEADING_CODE_P (c))
{
unsigned char *ptr;
int bytes, width, wide_column;
scan_byte--;
ptr = BYTE_POS_ADDR (scan_byte);
MULTIBYTE_BYTES_WIDTH (ptr, dp);
scan_byte += bytes;
col += width;
}
else if (VECTORP (charvec))
++col;
else if (ctl_arrow && (c < 040 || c == 0177))
col += 2;
else if (c < 040 || c >= 0177)
col += 4;
else
col++;
}
}
endloop:
last_known_column = col;
last_known_column_point = PT;
last_known_column_modified = MODIFF;
return col;
}
#if 0
static int
string_display_width (string, beg, end)
Lisp_Object string, beg, end;
{
register int col;
register unsigned char *ptr, *stop;
register int tab_seen;
int post_tab;
register int c;
register int tab_width = XINT (current_buffer->tab_width);
int ctl_arrow = !NILP (current_buffer->ctl_arrow);
register struct Lisp_Char_Table *dp = buffer_display_table ();
int b, e;
if (NILP (end))
e = XSTRING (string)->size;
else
{
CHECK_NUMBER (end, 0);
e = XINT (end);
}
if (NILP (beg))
b = 0;
else
{
CHECK_NUMBER (beg, 0);
b = XINT (beg);
}
ptr = XSTRING (string)->data + e;
stop = XSTRING (string)->data + b;
if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
col = 0, tab_seen = 0, post_tab = 0;
while (1)
{
if (ptr == stop)
break;
c = *--ptr;
if (dp != 0 && VECTORP (DISP_CHAR_VECTOR (dp, c)))
col += XVECTOR (DISP_CHAR_VECTOR (dp, c))->size;
else if (c >= 040 && c < 0177)
col++;
else if (c == '\n')
break;
else if (c == '\t')
{
if (tab_seen)
col = ((col + tab_width) / tab_width) * tab_width;
post_tab += col;
col = 0;
tab_seen = 1;
}
else
col += (ctl_arrow && c < 0200) ? 2 : 4;
}
if (tab_seen)
{
col = ((col + tab_width) / tab_width) * tab_width;
col += post_tab;
}
return col;
}
#endif
DEFUN ("indent-to", Findent_to, Sindent_to, 1, 2, "NIndent to column: ",
"Indent from point with tabs and spaces until COLUMN is reached.\n\
Optional second argument MININUM says always do at least MININUM spaces\n\
even if that goes past COLUMN; by default, MININUM is zero.")
(column, minimum)
Lisp_Object column, minimum;
{
int mincol;
register int fromcol;
register int tab_width = XINT (current_buffer->tab_width);
CHECK_NUMBER (column, 0);
if (NILP (minimum))
XSETFASTINT (minimum, 0);
CHECK_NUMBER (minimum, 1);
fromcol = current_column ();
mincol = fromcol + XINT (minimum);
if (mincol < XINT (column)) mincol = XINT (column);
if (fromcol == mincol)
return make_number (mincol);
if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
if (indent_tabs_mode)
{
Lisp_Object n;
XSETFASTINT (n, mincol / tab_width - fromcol / tab_width);
if (XFASTINT (n) != 0)
{
Finsert_char (make_number ('\t'), n, Qt);
fromcol = (mincol / tab_width) * tab_width;
}
}
XSETFASTINT (column, mincol - fromcol);
Finsert_char (make_number (' '), column, Qt);
last_known_column = mincol;
last_known_column_point = PT;
last_known_column_modified = MODIFF;
XSETINT (column, mincol);
return column;
}
static int position_indentation P_ ((int));
DEFUN ("current-indentation", Fcurrent_indentation, Scurrent_indentation,
0, 0, 0,
"Return the indentation of the current line.\n\
This is the horizontal position of the character\n\
following any initial whitespace.")
()
{
Lisp_Object val;
int opoint = PT, opoint_byte = PT_BYTE;
scan_newline (PT, PT_BYTE, BEGV, BEGV_BYTE, -1, 1);
XSETFASTINT (val, position_indentation (PT_BYTE));
SET_PT_BOTH (opoint, opoint_byte);
return val;
}
static int
position_indentation (pos_byte)
register int pos_byte;
{
register int column = 0;
register int tab_width = XINT (current_buffer->tab_width);
register unsigned char *p;
register unsigned char *stop;
unsigned char *start;
int next_boundary_byte = pos_byte;
int ceiling = next_boundary_byte;
if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
p = BYTE_POS_ADDR (pos_byte);
stop = p;
start = p;
while (1)
{
while (p == stop)
{
int stop_pos_byte;
if (p != start)
pos_byte = PTR_BYTE_POS (p);
if (pos_byte == ZV_BYTE)
return column;
if (pos_byte == next_boundary_byte)
{
int next_boundary;
int pos = BYTE_TO_CHAR (pos_byte);
pos = skip_invisible (pos, &next_boundary, ZV, Qnil);
pos_byte = CHAR_TO_BYTE (pos);
next_boundary_byte = CHAR_TO_BYTE (next_boundary);
}
if (pos_byte >= ceiling)
ceiling = BUFFER_CEILING_OF (pos_byte) + 1;
stop_pos_byte = min (ceiling, next_boundary_byte);
stop = BYTE_POS_ADDR (stop_pos_byte - 1) + 1;
p = BYTE_POS_ADDR (pos_byte);
}
switch (*p++)
{
case 0240:
if (! NILP (current_buffer->enable_multibyte_characters))
return column;
case ' ':
column++;
break;
case '\t':
column += tab_width - column % tab_width;
break;
default:
if (ASCII_BYTE_P (p[-1])
|| NILP (current_buffer->enable_multibyte_characters))
return column;
{
int c;
pos_byte = PTR_BYTE_POS (p - 1);
c = FETCH_MULTIBYTE_CHAR (pos_byte);
if (CHAR_HAS_CATEGORY (c, ' '))
{
column++;
INC_POS (pos_byte);
p = BYTE_POS_ADDR (pos_byte);
}
else
return column;
}
}
}
}
int
indented_beyond_p (pos, pos_byte, column)
int pos, pos_byte, column;
{
int val;
int opoint = PT, opoint_byte = PT_BYTE;
SET_PT_BOTH (pos, pos_byte);
while (PT > BEGV && FETCH_BYTE (PT_BYTE) == '\n')
scan_newline (PT - 1, PT_BYTE - 1, BEGV, BEGV_BYTE, -1, 0);
val = position_indentation (PT_BYTE);
SET_PT_BOTH (opoint, opoint_byte);
return val >= column;
}
DEFUN ("move-to-column", Fmove_to_column, Smove_to_column, 1, 2, "p",
"Move point to column COLUMN in the current line.\n\
The column of a character is calculated by adding together the widths\n\
as displayed of the previous characters in the line.\n\
This function ignores line-continuation;\n\
there is no upper limit on the column number a character can have\n\
and horizontal scrolling has no effect.\n\
\n\
If specified column is within a character, point goes after that character.\n\
If it's past end of line, point goes to end of line.\n\n\
A non-nil second (optional) argument FORCE means,\n\
if COLUMN is in the middle of a tab character, change it to spaces.\n\
In addition, if FORCE is t, and the line is too short\n\
to reach column COLUMN, add spaces/tabs to get there.\n\
\n\
The return value is the current column.")
(column, force)
Lisp_Object column, force;
{
register int pos;
register int col = current_column ();
register int goal;
register int end;
register int tab_width = XINT (current_buffer->tab_width);
register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
register struct Lisp_Char_Table *dp = buffer_display_table ();
register int multibyte = !NILP (current_buffer->enable_multibyte_characters);
Lisp_Object val;
int prev_col = 0;
int c = 0;
int next_boundary;
int pos_byte, end_byte, next_boundary_byte;
if (tab_width <= 0 || tab_width > 1000) tab_width = 8;
CHECK_NATNUM (column, 0);
goal = XINT (column);
pos = PT;
pos_byte = PT_BYTE;
end = ZV;
end_byte = ZV_BYTE;
next_boundary = pos;
next_boundary_byte = PT_BYTE;
if (col > goal)
{
end = pos;
pos = current_column_bol_cache;
pos_byte = CHAR_TO_BYTE (pos);
col = 0;
}
while (pos < end)
{
Lisp_Object charvec;
EMACS_INT i, n;
while (pos == next_boundary)
{
int prev = pos;
pos = skip_invisible (pos, &next_boundary, end, Qnil);
if (pos != prev)
pos_byte = CHAR_TO_BYTE (pos);
next_boundary_byte = CHAR_TO_BYTE (next_boundary);
if (pos >= end)
goto endloop;
}
if (col >= goal)
break;
{
int len, len_byte, width;
if (check_composition (pos, pos_byte, Z, &len, &len_byte, &width))
{
pos += len;
pos_byte += len_byte;
col += width;
continue;
}
}
c = FETCH_BYTE (pos_byte);
if (dp != 0
&& ! (multibyte && BASE_LEADING_CODE_P (c))
&& VECTORP (DISP_CHAR_VECTOR (dp, c)))
{
charvec = DISP_CHAR_VECTOR (dp, c);
n = ASIZE (charvec);
}
else
{
charvec = Qnil;
n = 1;
}
for (i = n - 1; i >= 0; --i)
{
if (VECTORP (charvec))
{
Lisp_Object entry = AREF (charvec, i);
if (INTEGERP (entry)
&& GLYPH_CHAR_VALID_P (XFASTINT (entry)))
c = FAST_GLYPH_CHAR (XFASTINT (entry));
else
c = ' ';
}
if (c == '\n')
goto endloop;
if (c == '\r' && EQ (current_buffer->selective_display, Qt))
goto endloop;
pos++;
pos_byte++;
if (c == '\t')
{
prev_col = col;
col += tab_width;
col = col / tab_width * tab_width;
}
else if (VECTORP (charvec))
++col;
else if (ctl_arrow && (c < 040 || c == 0177))
col += 2;
else if (c < 040 || c == 0177)
col += 4;
else if (c < 0177)
col++;
else if (multibyte && BASE_LEADING_CODE_P (c))
{
unsigned char *ptr;
int bytes, width, wide_column;
pos_byte--;
ptr = BYTE_POS_ADDR (pos_byte);
MULTIBYTE_BYTES_WIDTH (ptr, dp);
pos_byte += bytes;
col += width;
}
else
col += 4;
}
}
endloop:
SET_PT_BOTH (pos, pos_byte);
if (!NILP (force) && col > goal && c == '\t' && prev_col < goal)
{
int goal_pt, goal_pt_byte;
SET_PT_BOTH (PT - 1, PT_BYTE - 1);
Finsert_char (make_number (' '), make_number (goal - prev_col), Qt);
del_range (PT, PT + 1);
goal_pt = PT;
goal_pt_byte = PT_BYTE;
Findent_to (make_number (col), Qnil);
SET_PT_BOTH (goal_pt, goal_pt_byte);
col = goal;
}
if (col < goal && EQ (force, Qt))
Findent_to (make_number (col = goal), Qnil);
last_known_column = col;
last_known_column_point = PT;
last_known_column_modified = MODIFF;
XSETFASTINT (val, col);
return val;
}
struct position val_compute_motion;
struct position *
compute_motion (from, fromvpos, fromhpos, did_motion, to, tovpos, tohpos, width, hscroll, tab_offset, win)
int from, fromvpos, fromhpos, to, tovpos, tohpos;
int did_motion;
register int width;
int hscroll, tab_offset;
struct window *win;
{
register int hpos = fromhpos;
register int vpos = fromvpos;
register int pos;
int pos_byte;
register int c = 0;
register int tab_width = XFASTINT (current_buffer->tab_width);
register int ctl_arrow = !NILP (current_buffer->ctl_arrow);
register struct Lisp_Char_Table *dp = window_display_table (win);
int selective
= (INTEGERP (current_buffer->selective_display)
? XINT (current_buffer->selective_display)
: !NILP (current_buffer->selective_display) ? -1 : 0);
int prev_hpos = 0;
int selective_rlen
= (selective && dp && VECTORP (DISP_INVIS_VECTOR (dp))
? XVECTOR (DISP_INVIS_VECTOR (dp))->size : 0);
int next_boundary = from;
int width_run_start = from;
int width_run_end = from;
int width_run_width = 0;
Lisp_Object *width_table;
Lisp_Object buffer;
int next_width_run = from;
Lisp_Object window;
int multibyte = !NILP (current_buffer->enable_multibyte_characters);
int wide_column_end_hpos = 0;
int prev_pos;
int prev_pos_byte;
int contin_hpos;
int prev_tab_offset;
XSETBUFFER (buffer, current_buffer);
XSETWINDOW (window, win);
width_run_cache_on_off ();
if (dp == buffer_display_table ())
width_table = (VECTORP (current_buffer->width_table)
? XVECTOR (current_buffer->width_table)->contents
: 0);
else
width_table = 0;
if (tab_width <= 0 || tab_width > 1000)
tab_width = 8;
immediate_quit = 1;
QUIT;
pos = prev_pos = from;
pos_byte = prev_pos_byte = CHAR_TO_BYTE (from);
contin_hpos = 0;
prev_tab_offset = tab_offset;
while (1)
{
while (pos == next_boundary)
{
int pos_here = pos;
int newpos;
if (vpos > tovpos || vpos == tovpos && hpos >= tohpos)
{
if (contin_hpos && prev_hpos == 0
&& hpos > tohpos
&& (contin_hpos == width || wide_column_end_hpos > width))
{
pos = prev_pos;
pos_byte = prev_pos_byte;
hpos = prev_hpos;
tab_offset = prev_tab_offset;
}
break;
}
if (!did_motion)
{
unsigned char *ovstr;
int ovlen = overlay_strings (pos, win, &ovstr);
hpos += ((multibyte && ovlen > 0)
? strwidth (ovstr, ovlen) : ovlen);
}
did_motion = 0;
if (pos >= to)
break;
newpos = skip_invisible (pos, &next_boundary, to, window);
if (newpos >= to)
{
pos = min (to, newpos);
pos_byte = CHAR_TO_BYTE (pos);
goto after_loop;
}
if (newpos != pos_here)
{
pos = newpos;
pos_byte = CHAR_TO_BYTE (pos);
}
}
if (hpos > width)
{
if (hscroll
|| (truncate_partial_width_windows
&& width + 1 < FRAME_WIDTH (XFRAME (WINDOW_FRAME (win))))
|| !NILP (current_buffer->truncate_lines))
{
if (pos <= to)
{
pos = find_before_next_newline (pos, to, 1);
pos_byte = CHAR_TO_BYTE (pos);
hpos = width;
if (pos >= next_boundary)
next_boundary = pos + 1;
prev_hpos = width;
prev_tab_offset = tab_offset;
}
}
else
{
prev_tab_offset = tab_offset;
if (wide_column_end_hpos > width)
{
hpos -= prev_hpos;
tab_offset += prev_hpos;
}
else
{
tab_offset += width;
hpos -= width;
}
vpos++;
contin_hpos = prev_hpos;
prev_hpos = 0;
}
}
if (pos > to)
{
pos = prev_pos;
pos_byte = prev_pos_byte;
hpos = prev_hpos;
tab_offset = prev_tab_offset;
if (contin_hpos && prev_hpos == 0
&& contin_hpos < width && !wide_column_end_hpos)
{
hpos = contin_hpos;
vpos = vpos - 1;
}
else if (c == '\n')
vpos = vpos - 1;
break;
}
if (vpos > tovpos || vpos == tovpos && hpos >= tohpos)
{
if (contin_hpos && prev_hpos == 0
&& hpos > tohpos
&& (contin_hpos == width || wide_column_end_hpos > width))
{
pos = prev_pos;
pos_byte = prev_pos_byte;
hpos = prev_hpos;
tab_offset = prev_tab_offset;
}
break;
}
if (pos == ZV)
break;
prev_hpos = hpos;
prev_pos = pos;
prev_pos_byte = pos_byte;
wide_column_end_hpos = 0;
if (current_buffer->width_run_cache && pos >= next_width_run)
{
int run_end;
int common_width
= region_cache_forward (current_buffer,
current_buffer->width_run_cache,
pos, &run_end);
if (common_width != 0)
{
int run_end_hpos;
if (run_end > to)
run_end = to;
run_end_hpos = hpos + (run_end - pos) * common_width;
if (vpos == tovpos && run_end_hpos > tohpos)
{
run_end = pos + (tohpos - hpos) / common_width;
run_end_hpos = hpos + (run_end - pos) * common_width;
}
if (run_end_hpos >= width)
{
run_end = pos + (width - hpos) / common_width;
run_end_hpos = hpos + (run_end - pos) * common_width;
}
hpos = run_end_hpos;
if (run_end > pos)
prev_hpos = hpos - common_width;
if (pos != run_end)
{
pos = run_end;
pos_byte = CHAR_TO_BYTE (pos);
}
}
next_width_run = run_end + 1;
}
else
{
EMACS_INT i, n;
Lisp_Object charvec;
c = FETCH_BYTE (pos_byte);
{
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;
}
}
pos++, pos_byte++;
if (current_buffer->width_run_cache)
{
if (pos - 1 == width_run_end
&& XFASTINT (width_table[c]) == width_run_width)
width_run_end = pos;
else
{
if (width_run_start < width_run_end
&& width_run_width == 1)
know_region_cache (current_buffer,
current_buffer->width_run_cache,
width_run_start, width_run_end);
width_run_width = XFASTINT (width_table[c]);
width_run_start = pos - 1;
width_run_end = pos;
}
}
if (dp != 0
&& ! (multibyte && BASE_LEADING_CODE_P (c))
&& VECTORP (DISP_CHAR_VECTOR (dp, c)))
{
charvec = DISP_CHAR_VECTOR (dp, c);
n = ASIZE (charvec);
}
else
{
charvec = Qnil;
n = 1;
}
for (i = n - 1; i >= 0; --i)
{
if (VECTORP (charvec))
{
Lisp_Object entry = AREF (charvec, i);
if (INTEGERP (entry)
&& GLYPH_CHAR_VALID_P (XFASTINT (entry)))
c = FAST_GLYPH_CHAR (XFASTINT (entry));
else
c = ' ';
}
if (c >= 040 && c < 0177)
hpos++;
else if (c == '\t')
{
int tem = ((hpos + tab_offset + hscroll - (hscroll > 0))
% tab_width);
if (tem < 0)
tem += tab_width;
hpos += tab_width - tem;
}
else if (c == '\n')
{
if (selective > 0
&& indented_beyond_p (pos, pos_byte, selective))
{
if (pos < to)
{
do
{
pos = find_before_next_newline (pos, to, 1);
if (pos < to)
pos++;
pos_byte = CHAR_TO_BYTE (pos);
}
while (pos < to
&& indented_beyond_p (pos, pos_byte, selective));
if (selective_rlen)
{
hpos += selective_rlen;
if (hpos >= width)
hpos = width;
}
DEC_BOTH (pos, pos_byte);
}
}
else
{
vpos++;
hpos = 0;
hpos -= hscroll;
if (hscroll > 0)
hpos++;
tab_offset = 0;
}
contin_hpos = 0;
}
else if (c == CR && selective < 0)
{
if (pos < to)
{
pos = find_before_next_newline (pos, to, 1);
pos_byte = CHAR_TO_BYTE (pos);
}
if (pos > next_boundary)
next_boundary = pos;
if (selective_rlen)
{
hpos += selective_rlen;
if (hpos >= width)
hpos = width;
}
}
else if (multibyte && BASE_LEADING_CODE_P (c))
{
unsigned char *ptr;
int bytes, width, wide_column;
pos_byte--;
ptr = BYTE_POS_ADDR (pos_byte);
MULTIBYTE_BYTES_WIDTH (ptr, dp);
pos_byte += bytes;
if (wide_column)
wide_column_end_hpos = hpos + wide_column;
hpos += width;
}
else if (VECTORP (charvec))
++hpos;
else
hpos += (ctl_arrow && c < 0200) ? 2 : 4;
}
}
}
after_loop:
if (current_buffer->width_run_cache
&& width_run_width == 1
&& width_run_start < width_run_end)
know_region_cache (current_buffer, current_buffer->width_run_cache,
width_run_start, width_run_end);
val_compute_motion.bufpos = pos;
val_compute_motion.bytepos = pos_byte;
val_compute_motion.hpos = hpos;
val_compute_motion.vpos = vpos;
if (contin_hpos && prev_hpos == 0)
val_compute_motion.prevhpos = contin_hpos;
else
val_compute_motion.prevhpos = prev_hpos;
val_compute_motion.ovstring_chars_done = 0;
val_compute_motion.contin = (contin_hpos && prev_hpos == 0);
immediate_quit = 0;
return &val_compute_motion;
}
#if 0
DEFUN ("compute-motion", Ffoo, Sfoo, 7, 7, 0,
"Scan through the current buffer, calculating screen position.\n\
Scan the current buffer forward from offset FROM,\n\
assuming it is at position FROMPOS--a cons of the form (HPOS . VPOS)--\n\
to position TO or position TOPOS--another cons of the form (HPOS . VPOS)--\n\
and return the ending buffer position and screen location.\n\
\n\
There are three additional arguments:\n\
\n\
WIDTH is the number of columns available to display text;\n\
this affects handling of continuation lines.\n\
This is usually the value returned by `window-width', less one (to allow\n\
for the continuation glyph).\n\
\n\
OFFSETS is either nil or a cons cell (HSCROLL . TAB-OFFSET).\n\
HSCROLL is the number of columns not being displayed at the left\n\
margin; this is usually taken from a window's hscroll member.\n\
TAB-OFFSET is the number of columns of the first tab that aren't\n\
being displayed, perhaps because the line was continued within it.\n\
If OFFSETS is nil, HSCROLL and TAB-OFFSET are assumed to be zero.\n\
\n\
WINDOW is the window to operate on. It is used to choose the display table;\n\
if it is showing the current buffer, it is used also for\n\
deciding which overlay properties apply.\n\
Note that `compute-motion' always operates on the current buffer.\n\
\n\
The value is a list of five elements:\n\
(POS HPOS VPOS PREVHPOS CONTIN)\n\
POS is the buffer position where the scan stopped.\n\
VPOS is the vertical position where the scan stopped.\n\
HPOS is the horizontal position where the scan stopped.\n\
\n\
PREVHPOS is the horizontal position one character back from POS.\n\
CONTIN is t if a line was continued after (or within) the previous character.\n\
\n\
For example, to find the buffer position of column COL of line LINE\n\
of a certain window, pass the window's starting location as FROM\n\
and the window's upper-left coordinates as FROMPOS.\n\
Pass the buffer's (point-max) as TO, to limit the scan to the end of the\n\
visible section of the buffer, and pass LINE and COL as TOPOS.")
(from, frompos, to, topos, width, offsets, window)
#endif
DEFUN ("compute-motion", Fcompute_motion, Scompute_motion, 7, 7, 0,
0)
(from, frompos, to, topos, width, offsets, window)
Lisp_Object from, frompos, to, topos;
Lisp_Object width, offsets, window;
{
Lisp_Object bufpos, hpos, vpos, prevhpos;
struct position *pos;
int hscroll, tab_offset;
CHECK_NUMBER_COERCE_MARKER (from, 0);
CHECK_CONS (frompos, 0);
CHECK_NUMBER (XCAR (frompos), 0);
CHECK_NUMBER (XCDR (frompos), 0);
CHECK_NUMBER_COERCE_MARKER (to, 0);
CHECK_CONS (topos, 0);
CHECK_NUMBER (XCAR (topos), 0);
CHECK_NUMBER (XCDR (topos), 0);
CHECK_NUMBER (width, 0);
if (!NILP (offsets))
{
CHECK_CONS (offsets, 0);
CHECK_NUMBER (XCAR (offsets), 0);
CHECK_NUMBER (XCDR (offsets), 0);
hscroll = XINT (XCAR (offsets));
tab_offset = XINT (XCDR (offsets));
}
else
hscroll = tab_offset = 0;
if (NILP (window))
window = Fselected_window ();
else
CHECK_LIVE_WINDOW (window, 0);
if (XINT (from) < BEGV || XINT (from) > ZV)
args_out_of_range_3 (from, make_number (BEGV), make_number (ZV));
if (XINT (to) < BEGV || XINT (to) > ZV)
args_out_of_range_3 (to, make_number (BEGV), make_number (ZV));
pos = compute_motion (XINT (from), XINT (XCDR (frompos)),
XINT (XCAR (frompos)), 0,
XINT (to), XINT (XCDR (topos)),
XINT (XCAR (topos)),
XINT (width), hscroll, tab_offset,
XWINDOW (window));
XSETFASTINT (bufpos, pos->bufpos);
XSETINT (hpos, pos->hpos);
XSETINT (vpos, pos->vpos);
XSETINT (prevhpos, pos->prevhpos);
return Fcons (bufpos,
Fcons (hpos,
Fcons (vpos,
Fcons (prevhpos,
Fcons (pos->contin ? Qt : Qnil, Qnil)))));
}
struct position val_vmotion;
struct position *
vmotion (from, vtarget, w)
register int from, vtarget;
struct window *w;
{
int width = window_internal_width (w) - 1;
int hscroll = XINT (w->hscroll);
struct position pos;
register int vpos = 0;
Lisp_Object prevline;
register int first;
int from_byte;
int lmargin = hscroll > 0 ? 1 - hscroll : 0;
int selective
= (INTEGERP (current_buffer->selective_display)
? XINT (current_buffer->selective_display)
: !NILP (current_buffer->selective_display) ? -1 : 0);
Lisp_Object window;
int start_hpos = 0;
int did_motion;
Lisp_Object text_prop_object;
XSETWINDOW (window, w);
if (EQ (w->buffer, Fcurrent_buffer ()))
text_prop_object = window;
else
text_prop_object = Fcurrent_buffer ();
if (vpos >= vtarget)
{
first = 1;
while ((vpos > vtarget || first) && from > BEGV)
{
Lisp_Object propval;
XSETFASTINT (prevline, find_next_newline_no_quit (from - 1, -1));
while (XFASTINT (prevline) > BEGV
&& ((selective > 0
&& indented_beyond_p (XFASTINT (prevline),
CHAR_TO_BYTE (XFASTINT (prevline)),
selective))
|| (propval = Fget_char_property (prevline,
Qinvisible,
text_prop_object),
TEXT_PROP_MEANS_INVISIBLE (propval))))
XSETFASTINT (prevline,
find_next_newline_no_quit (XFASTINT (prevline) - 1,
-1));
pos = *compute_motion (XFASTINT (prevline), 0,
lmargin + (XFASTINT (prevline) == BEG
? start_hpos : 0),
0,
from,
1 << (BITS_PER_SHORT - 1),
1 << (BITS_PER_SHORT - 1),
width, hscroll,
(XFASTINT (prevline) == BEG
? -start_hpos : 0),
w);
vpos -= pos.vpos;
first = 0;
from = XFASTINT (prevline);
}
if (vpos >= vtarget)
{
val_vmotion.bufpos = from;
val_vmotion.bytepos = CHAR_TO_BYTE (from);
val_vmotion.vpos = vpos;
val_vmotion.hpos = lmargin;
val_vmotion.contin = 0;
val_vmotion.prevhpos = 0;
val_vmotion.ovstring_chars_done = 0;
val_vmotion.tab_offset = 0;
return &val_vmotion;
}
}
from_byte = CHAR_TO_BYTE (from);
if (from > BEGV && FETCH_BYTE (from_byte - 1) != '\n')
{
Lisp_Object propval;
XSETFASTINT (prevline, find_next_newline_no_quit (from, -1));
while (XFASTINT (prevline) > BEGV
&& ((selective > 0
&& indented_beyond_p (XFASTINT (prevline),
CHAR_TO_BYTE (XFASTINT (prevline)),
selective))
|| (propval = Fget_char_property (prevline, Qinvisible,
text_prop_object),
TEXT_PROP_MEANS_INVISIBLE (propval))))
XSETFASTINT (prevline,
find_next_newline_no_quit (XFASTINT (prevline) - 1,
-1));
pos = *compute_motion (XFASTINT (prevline), 0,
lmargin + (XFASTINT (prevline) == BEG
? start_hpos : 0),
0,
from,
1 << (BITS_PER_SHORT - 1),
1 << (BITS_PER_SHORT - 1),
width, hscroll,
(XFASTINT (prevline) == BEG ? -start_hpos : 0),
w);
did_motion = 1;
}
else
{
pos.hpos = lmargin + (from == BEG ? start_hpos : 0);
pos.vpos = 0;
pos.tab_offset = 0;
did_motion = 0;
}
return compute_motion (from, vpos, pos.hpos, did_motion,
ZV, vtarget, - (1 << (BITS_PER_SHORT - 1)),
width, hscroll,
pos.tab_offset - (from == BEG ? start_hpos : 0),
w);
}
DEFUN ("vertical-motion", Fvertical_motion, Svertical_motion, 1, 2, 0,
"Move point to start of the screen line LINES lines down.\n\
If LINES is negative, this means moving up.\n\
\n\
This function is an ordinary cursor motion function\n\
which calculates the new position based on how text would be displayed.\n\
The new position may be the start of a line,\n\
or just the start of a continuation line.\n\
The function returns number of screen lines moved over;\n\
that usually equals LINES, but may be closer to zero\n\
if beginning or end of buffer was reached.\n\
\n\
The optional second argument WINDOW specifies the window to use for\n\
parameters such as width, horizontal scrolling, and so on.\n\
The default is to use the selected window's parameters.\n\
\n\
`vertical-motion' always uses the current buffer,\n\
regardless of which buffer is displayed in WINDOW.\n\
This is consistent with other cursor motion functions\n\
and makes it possible to use `vertical-motion' in any buffer,\n\
whether or not it is currently displayed in some window.")
(lines, window)
Lisp_Object lines, window;
{
struct it it;
struct text_pos pt;
struct window *w;
Lisp_Object old_buffer;
struct gcpro gcpro1;
CHECK_NUMBER (lines, 0);
if (! NILP (window))
CHECK_WINDOW (window, 0);
else
window = selected_window;
w = XWINDOW (window);
old_buffer = Qnil;
GCPRO1 (old_buffer);
if (XBUFFER (w->buffer) != current_buffer)
{
old_buffer = w->buffer;
XSETBUFFER (w->buffer, current_buffer);
}
SET_TEXT_POS (pt, PT, PT_BYTE);
start_display (&it, w, pt);
move_it_by_lines (&it, XINT (lines), 0);
SET_PT_BOTH (IT_CHARPOS (it), IT_BYTEPOS (it));
if (BUFFERP (old_buffer))
w->buffer = old_buffer;
RETURN_UNGCPRO (make_number (it.vpos));
}
void
syms_of_indent ()
{
DEFVAR_BOOL ("indent-tabs-mode", &indent_tabs_mode,
"*Indentation can insert tabs if this is non-nil.\n\
Setting this variable automatically makes it local to the current buffer.");
indent_tabs_mode = 1;
defsubr (&Scurrent_indentation);
defsubr (&Sindent_to);
defsubr (&Scurrent_column);
defsubr (&Smove_to_column);
defsubr (&Svertical_motion);
defsubr (&Scompute_motion);
}