mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-12-28 10:56:36 +00:00
Initial version of native display of line numbers
* src/xdisp.c (syms_of_xdisp) <display-line-numbers>: New buffer-local variable. Include <math.h>. (maybe_produce_line_number): New function. (DISP_INFINITY): Rename from INFINITY, since math.h defines INFINITY. (try_window_reusing_current_matrix): Don't use this method when display-line-numbers is in effect. * src/dispextern.h (struct it): New members 'lnum'.
This commit is contained in:
parent
7a2038d7c8
commit
daf78963ee
@ -384,6 +384,7 @@ struct glyph
|
|||||||
glyph standing for newline at end of line 0
|
glyph standing for newline at end of line 0
|
||||||
empty space after the end of the line -1
|
empty space after the end of the line -1
|
||||||
overlay arrow on a TTY -1
|
overlay arrow on a TTY -1
|
||||||
|
glyph displaying line number -1
|
||||||
glyph at EOB that ends in a newline -1
|
glyph at EOB that ends in a newline -1
|
||||||
left truncation glyphs: -1
|
left truncation glyphs: -1
|
||||||
right truncation/continuation glyphs next buffer position
|
right truncation/continuation glyphs next buffer position
|
||||||
@ -2571,7 +2572,12 @@ struct it
|
|||||||
Do NOT use !BUFFERP (it.object) as a test whether we are
|
Do NOT use !BUFFERP (it.object) as a test whether we are
|
||||||
iterating over a string; use STRINGP (it.string) instead.
|
iterating over a string; use STRINGP (it.string) instead.
|
||||||
|
|
||||||
Position is the current iterator position in object. */
|
Position is the current iterator position in object.
|
||||||
|
|
||||||
|
The 'position's CHARPOS is copied to glyph->charpos of the glyph
|
||||||
|
produced by PRODUCE_GLYPHS, so any artificial value documented
|
||||||
|
under 'struct glyph's 'charpos' member can also be found in the
|
||||||
|
'position' member here. */
|
||||||
Lisp_Object object;
|
Lisp_Object object;
|
||||||
struct text_pos position;
|
struct text_pos position;
|
||||||
|
|
||||||
@ -2655,6 +2661,16 @@ struct it
|
|||||||
coordinate is past first_visible_x. */
|
coordinate is past first_visible_x. */
|
||||||
int hpos;
|
int hpos;
|
||||||
|
|
||||||
|
/* Current line number, zero-based. */
|
||||||
|
ptrdiff_t lnum;
|
||||||
|
|
||||||
|
/* The byte position corresponding to lnum. */
|
||||||
|
ptrdiff_t lnum_bytepos;
|
||||||
|
|
||||||
|
/* The width in columns needed for display of the line numbers, or
|
||||||
|
zero if not computed. */
|
||||||
|
int lnum_width;
|
||||||
|
|
||||||
/* Left fringe bitmap number (enum fringe_bitmap_type). */
|
/* Left fringe bitmap number (enum fringe_bitmap_type). */
|
||||||
unsigned left_user_fringe_bitmap : FRINGE_ID_BITS;
|
unsigned left_user_fringe_bitmap : FRINGE_ID_BITS;
|
||||||
|
|
||||||
|
176
src/xdisp.c
176
src/xdisp.c
@ -290,6 +290,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
|||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#include "lisp.h"
|
#include "lisp.h"
|
||||||
#include "atimer.h"
|
#include "atimer.h"
|
||||||
@ -324,7 +325,7 @@ along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>. */
|
|||||||
#define FRAME_X_OUTPUT(f) ((f)->output_data.x)
|
#define FRAME_X_OUTPUT(f) ((f)->output_data.x)
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#define INFINITY 10000000
|
#define DISP_INFINITY 10000000
|
||||||
|
|
||||||
/* Holds the list (error). */
|
/* Holds the list (error). */
|
||||||
static Lisp_Object list_of_error;
|
static Lisp_Object list_of_error;
|
||||||
@ -843,6 +844,8 @@ static const char *decode_mode_spec (struct window *, int, int, Lisp_Object *);
|
|||||||
static void display_menu_bar (struct window *);
|
static void display_menu_bar (struct window *);
|
||||||
static ptrdiff_t display_count_lines (ptrdiff_t, ptrdiff_t, ptrdiff_t,
|
static ptrdiff_t display_count_lines (ptrdiff_t, ptrdiff_t, ptrdiff_t,
|
||||||
ptrdiff_t *);
|
ptrdiff_t *);
|
||||||
|
static void pint2str (register char *, register int, register ptrdiff_t);
|
||||||
|
|
||||||
static int display_string (const char *, Lisp_Object, Lisp_Object,
|
static int display_string (const char *, Lisp_Object, Lisp_Object,
|
||||||
ptrdiff_t, ptrdiff_t, struct it *, int, int, int, int);
|
ptrdiff_t, ptrdiff_t, struct it *, int, int, int, int);
|
||||||
static void compute_line_metrics (struct it *);
|
static void compute_line_metrics (struct it *);
|
||||||
@ -6751,7 +6754,7 @@ reseat_to_string (struct it *it, const char *s, Lisp_Object string,
|
|||||||
FIELD_WIDTH < 0 means infinite field width. This is useful for
|
FIELD_WIDTH < 0 means infinite field width. This is useful for
|
||||||
padding with `-' at the end of a mode line. */
|
padding with `-' at the end of a mode line. */
|
||||||
if (field_width < 0)
|
if (field_width < 0)
|
||||||
field_width = INFINITY;
|
field_width = DISP_INFINITY;
|
||||||
/* Implementation note: We deliberately don't enlarge
|
/* Implementation note: We deliberately don't enlarge
|
||||||
it->bidi_it.string.schars here to fit it->end_charpos, because
|
it->bidi_it.string.schars here to fit it->end_charpos, because
|
||||||
the bidi iterator cannot produce characters out of thin air. */
|
the bidi iterator cannot produce characters out of thin air. */
|
||||||
@ -13138,7 +13141,7 @@ hscroll_window_tree (Lisp_Object window)
|
|||||||
if (hscl)
|
if (hscl)
|
||||||
it.first_visible_x = window_hscroll_limited (w, it.f)
|
it.first_visible_x = window_hscroll_limited (w, it.f)
|
||||||
* FRAME_COLUMN_WIDTH (it.f);
|
* FRAME_COLUMN_WIDTH (it.f);
|
||||||
it.last_visible_x = INFINITY;
|
it.last_visible_x = DISP_INFINITY;
|
||||||
move_it_in_display_line_to (&it, pt, -1, MOVE_TO_POS);
|
move_it_in_display_line_to (&it, pt, -1, MOVE_TO_POS);
|
||||||
/* If the line ends in an overlay string with a newline,
|
/* If the line ends in an overlay string with a newline,
|
||||||
we might infloop, because displaying the window will
|
we might infloop, because displaying the window will
|
||||||
@ -15823,7 +15826,7 @@ compute_window_start_on_continuation_line (struct window *w)
|
|||||||
So, we're looking for the display line start with the
|
So, we're looking for the display line start with the
|
||||||
minimum distance from the old window start. */
|
minimum distance from the old window start. */
|
||||||
pos_before_pt = pos = it.current.pos;
|
pos_before_pt = pos = it.current.pos;
|
||||||
min_distance = INFINITY;
|
min_distance = DISP_INFINITY;
|
||||||
while ((distance = eabs (CHARPOS (start_pos) - IT_CHARPOS (it))),
|
while ((distance = eabs (CHARPOS (start_pos) - IT_CHARPOS (it))),
|
||||||
distance < min_distance)
|
distance < min_distance)
|
||||||
{
|
{
|
||||||
@ -17593,6 +17596,12 @@ try_window_reusing_current_matrix (struct window *w)
|
|||||||
if (w->vscroll || MATRIX_ROW_PARTIALLY_VISIBLE_P (w, start_row))
|
if (w->vscroll || MATRIX_ROW_PARTIALLY_VISIBLE_P (w, start_row))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
/* Give up if line numbers are being displayed, because reusing the
|
||||||
|
current matrix might use the wrong width for line-number
|
||||||
|
display. */
|
||||||
|
if (!NILP (Vdisplay_line_numbers))
|
||||||
|
return false;
|
||||||
|
|
||||||
/* The variable new_start now holds the new window start. The old
|
/* The variable new_start now holds the new window start. The old
|
||||||
start `start' can be determined from the current matrix. */
|
start `start' can be determined from the current matrix. */
|
||||||
SET_TEXT_POS_FROM_MARKER (new_start, w->start);
|
SET_TEXT_POS_FROM_MARKER (new_start, w->start);
|
||||||
@ -20670,6 +20679,141 @@ find_row_edges (struct it *it, struct glyph_row *row,
|
|||||||
row->maxpos = it->current.pos;
|
row->maxpos = it->current.pos;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
maybe_produce_line_number (struct it *it)
|
||||||
|
{
|
||||||
|
ptrdiff_t last_line = it->lnum;
|
||||||
|
ptrdiff_t start_from, bytepos;
|
||||||
|
|
||||||
|
/* FIXME: Maybe reuse the data in it->w->base_line_number. */
|
||||||
|
if (!last_line)
|
||||||
|
start_from = BEGV;
|
||||||
|
else
|
||||||
|
start_from = it->lnum_bytepos;
|
||||||
|
|
||||||
|
/* Paranoia: what if someone changes the narrowing since the last
|
||||||
|
time display_line was called? Shouldn't really happen, but who
|
||||||
|
knows what some crazy Lisp invoked by :eval could do? */
|
||||||
|
if (!(BEGV_BYTE <= start_from && start_from < ZV_BYTE))
|
||||||
|
{
|
||||||
|
last_line = 0;
|
||||||
|
start_from = BEGV_BYTE;
|
||||||
|
}
|
||||||
|
|
||||||
|
ptrdiff_t this_line;
|
||||||
|
|
||||||
|
this_line =
|
||||||
|
last_line + display_count_lines (start_from,
|
||||||
|
IT_BYTEPOS (*it), IT_CHARPOS (*it),
|
||||||
|
&bytepos);
|
||||||
|
eassert (this_line > 0 || (this_line == 0 && start_from == BEGV_BYTE));
|
||||||
|
eassert (bytepos == IT_BYTEPOS (*it));
|
||||||
|
|
||||||
|
/* If this is a new logical line, produce the glyphs for the line
|
||||||
|
number. */
|
||||||
|
if (this_line != last_line || !last_line || it->continuation_lines_width > 0)
|
||||||
|
{
|
||||||
|
if (this_line != last_line || !last_line)
|
||||||
|
{
|
||||||
|
it->lnum = this_line;
|
||||||
|
it->lnum_bytepos = IT_BYTEPOS (*it);
|
||||||
|
}
|
||||||
|
|
||||||
|
void *itdata = bidi_shelve_cache ();
|
||||||
|
struct it tem_it;
|
||||||
|
char lnum_buf[INT_STRLEN_BOUND (ptrdiff_t) + 1];
|
||||||
|
bool beyond_zv = IT_BYTEPOS (*it) >= ZV_BYTE ? true : false;
|
||||||
|
/* Compute the required width if needed. */
|
||||||
|
if (!it->lnum_width)
|
||||||
|
{
|
||||||
|
/* Max line number to be displayed cannot be more than the
|
||||||
|
one corresponding to the last row of the desired
|
||||||
|
matrix. */
|
||||||
|
ptrdiff_t max_lnum =
|
||||||
|
this_line + it->w->desired_matrix->nrows - 1 - it->vpos;
|
||||||
|
it->lnum_width = log10 (max_lnum) + 1;
|
||||||
|
eassert (it->lnum_width > 0);
|
||||||
|
}
|
||||||
|
pint2str (lnum_buf, it->lnum_width, this_line + 1);
|
||||||
|
/* Append a blank. */
|
||||||
|
strcat (lnum_buf, " ");
|
||||||
|
|
||||||
|
/* Setup for producing the glyphs. */
|
||||||
|
init_iterator (&tem_it, it->w, -1, -1, &scratch_glyph_row,
|
||||||
|
/* FIXME: Use specialized face. */
|
||||||
|
DEFAULT_FACE_ID);
|
||||||
|
scratch_glyph_row.reversed_p = false;
|
||||||
|
scratch_glyph_row.used[TEXT_AREA] = 0;
|
||||||
|
SET_TEXT_POS (tem_it.position, 0, 0);
|
||||||
|
tem_it.bidi_it.type = WEAK_EN;
|
||||||
|
/* According to UAX#9, EN goes up 2 levels in L2R paragraph and
|
||||||
|
1 level in R2L paragraphs. Emulate that. */
|
||||||
|
tem_it.bidi_it.resolved_level = 2;
|
||||||
|
if (it->glyph_row && it->glyph_row->reversed_p)
|
||||||
|
tem_it.bidi_it.resolved_level = 1;
|
||||||
|
|
||||||
|
/* Produce glyphs for the line number in a scratch glyph_row. */
|
||||||
|
int n_glyphs_before;
|
||||||
|
for (const char *p = lnum_buf; *p; p++)
|
||||||
|
{
|
||||||
|
/* For continuation lines and lines after ZV, instead of a
|
||||||
|
line number, produce a blank prefix of the same width. */
|
||||||
|
if (beyond_zv || it->continuation_lines_width > 0)
|
||||||
|
tem_it.c = tem_it.char_to_display = ' ';
|
||||||
|
else
|
||||||
|
tem_it.c = tem_it.char_to_display = *p;
|
||||||
|
tem_it.len = 1;
|
||||||
|
n_glyphs_before = scratch_glyph_row.used[TEXT_AREA];
|
||||||
|
/* Make sure these glyphs will have a "position" of -1. */
|
||||||
|
SET_TEXT_POS (tem_it.position, -1, -1);
|
||||||
|
PRODUCE_GLYPHS (&tem_it);
|
||||||
|
|
||||||
|
/* Stop producing glyphs if we don't have enough space on
|
||||||
|
this line. FIXME: should we refrain from producing the
|
||||||
|
line number at all in that case? */
|
||||||
|
if (tem_it.current_x > tem_it.last_visible_x)
|
||||||
|
{
|
||||||
|
scratch_glyph_row.used[TEXT_AREA] = n_glyphs_before;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Copy the produced glyphs into IT's glyph_row. */
|
||||||
|
struct glyph *g = scratch_glyph_row.glyphs[TEXT_AREA];
|
||||||
|
struct glyph *e = g + scratch_glyph_row.used[TEXT_AREA];
|
||||||
|
struct glyph *p = it->glyph_row ? it->glyph_row->glyphs[TEXT_AREA] : NULL;
|
||||||
|
short *u = it->glyph_row ? &it->glyph_row->used[TEXT_AREA] : NULL;
|
||||||
|
|
||||||
|
while (g < e)
|
||||||
|
{
|
||||||
|
it->current_x += g->pixel_width;
|
||||||
|
it->hpos++;
|
||||||
|
if (p)
|
||||||
|
{
|
||||||
|
*p++ = *g++;
|
||||||
|
(*u)++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Update IT->glyph_row's metrics. */
|
||||||
|
if (it->glyph_row)
|
||||||
|
{
|
||||||
|
struct glyph_row *row = it->glyph_row;
|
||||||
|
|
||||||
|
row->ascent = max (row->ascent, tem_it.max_ascent);
|
||||||
|
row->height = max (row->height,
|
||||||
|
tem_it.max_ascent + tem_it.max_descent);
|
||||||
|
row->phys_ascent = max (row->phys_ascent, tem_it.max_phys_ascent);
|
||||||
|
row->phys_height = max (row->phys_height,
|
||||||
|
tem_it.max_phys_ascent + tem_it.max_phys_descent);
|
||||||
|
row->extra_line_spacing = max (row->extra_line_spacing,
|
||||||
|
tem_it.max_extra_line_spacing);
|
||||||
|
}
|
||||||
|
|
||||||
|
bidi_unshelve_cache (itdata, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Construct the glyph row IT->glyph_row in the desired matrix of
|
/* Construct the glyph row IT->glyph_row in the desired matrix of
|
||||||
IT->w from text at the current position of IT. See dispextern.h
|
IT->w from text at the current position of IT. See dispextern.h
|
||||||
for an overview of struct it. Value is true if
|
for an overview of struct it. Value is true if
|
||||||
@ -20775,9 +20919,17 @@ display_line (struct it *it, int cursor_vpos)
|
|||||||
are hscrolled to the left of the left edge of the window. */
|
are hscrolled to the left of the left edge of the window. */
|
||||||
min_pos = CHARPOS (this_line_min_pos);
|
min_pos = CHARPOS (this_line_min_pos);
|
||||||
min_bpos = BYTEPOS (this_line_min_pos);
|
min_bpos = BYTEPOS (this_line_min_pos);
|
||||||
|
|
||||||
|
/* Produce line number, if needed. */
|
||||||
|
if (!NILP (Vdisplay_line_numbers))
|
||||||
|
maybe_produce_line_number (it);
|
||||||
}
|
}
|
||||||
else if (it->area == TEXT_AREA)
|
else if (it->area == TEXT_AREA)
|
||||||
{
|
{
|
||||||
|
/* Line numbers should precede the line-prefix or wrap-prefix. */
|
||||||
|
if (!NILP (Vdisplay_line_numbers))
|
||||||
|
maybe_produce_line_number (it);
|
||||||
|
|
||||||
/* We only do this when not calling move_it_in_display_line_to
|
/* We only do this when not calling move_it_in_display_line_to
|
||||||
above, because that function calls itself handle_line_prefix. */
|
above, because that function calls itself handle_line_prefix. */
|
||||||
handle_line_prefix (it);
|
handle_line_prefix (it);
|
||||||
@ -20936,6 +21088,10 @@ display_line (struct it *it, int cursor_vpos)
|
|||||||
process the prefix now. */
|
process the prefix now. */
|
||||||
if (it->area == TEXT_AREA && pending_handle_line_prefix)
|
if (it->area == TEXT_AREA && pending_handle_line_prefix)
|
||||||
{
|
{
|
||||||
|
/* Line numbers should precede the line-prefix or wrap-prefix. */
|
||||||
|
if (!NILP (Vdisplay_line_numbers))
|
||||||
|
maybe_produce_line_number (it);
|
||||||
|
|
||||||
pending_handle_line_prefix = false;
|
pending_handle_line_prefix = false;
|
||||||
handle_line_prefix (it);
|
handle_line_prefix (it);
|
||||||
}
|
}
|
||||||
@ -22007,7 +22163,7 @@ Value is the new character position of point. */)
|
|||||||
reach point, in order to start from its X coordinate. So we
|
reach point, in order to start from its X coordinate. So we
|
||||||
need to disregard the window's horizontal extent in that case. */
|
need to disregard the window's horizontal extent in that case. */
|
||||||
if (it.line_wrap == TRUNCATE)
|
if (it.line_wrap == TRUNCATE)
|
||||||
it.last_visible_x = INFINITY;
|
it.last_visible_x = DISP_INFINITY;
|
||||||
|
|
||||||
if (it.cmp_it.id < 0
|
if (it.cmp_it.id < 0
|
||||||
&& it.method == GET_FROM_STRING
|
&& it.method == GET_FROM_STRING
|
||||||
@ -22100,7 +22256,7 @@ Value is the new character position of point. */)
|
|||||||
{
|
{
|
||||||
start_display (&it, w, pt);
|
start_display (&it, w, pt);
|
||||||
if (it.line_wrap == TRUNCATE)
|
if (it.line_wrap == TRUNCATE)
|
||||||
it.last_visible_x = INFINITY;
|
it.last_visible_x = DISP_INFINITY;
|
||||||
reseat_at_previous_visible_line_start (&it);
|
reseat_at_previous_visible_line_start (&it);
|
||||||
it.current_x = it.current_y = it.hpos = 0;
|
it.current_x = it.current_y = it.hpos = 0;
|
||||||
if (pt_vpos != 0)
|
if (pt_vpos != 0)
|
||||||
@ -32134,6 +32290,14 @@ To add a prefix to continuation lines, use `wrap-prefix'. */);
|
|||||||
DEFSYM (Qline_prefix, "line-prefix");
|
DEFSYM (Qline_prefix, "line-prefix");
|
||||||
Fmake_variable_buffer_local (Qline_prefix);
|
Fmake_variable_buffer_local (Qline_prefix);
|
||||||
|
|
||||||
|
DEFVAR_LISP ("display-line-numbers", Vdisplay_line_numbers,
|
||||||
|
doc: /* Non-nil means display line numbers.
|
||||||
|
Line numbers are displayed before each non-continuation line, i.e.
|
||||||
|
after each newline that comes from buffer text. */);
|
||||||
|
Vdisplay_line_numbers = Qnil;
|
||||||
|
DEFSYM (Qdisplay_line_numbers, "display-line-numbers");
|
||||||
|
Fmake_variable_buffer_local (Qdisplay_line_numbers);
|
||||||
|
|
||||||
DEFVAR_BOOL ("inhibit-eval-during-redisplay", inhibit_eval_during_redisplay,
|
DEFVAR_BOOL ("inhibit-eval-during-redisplay", inhibit_eval_during_redisplay,
|
||||||
doc: /* Non-nil means don't eval Lisp during redisplay. */);
|
doc: /* Non-nil means don't eval Lisp during redisplay. */);
|
||||||
inhibit_eval_during_redisplay = false;
|
inhibit_eval_during_redisplay = false;
|
||||||
|
Loading…
Reference in New Issue
Block a user