1
Fork 0
mirror of git://git.sv.gnu.org/emacs.git synced 2025-12-15 10:30:25 -08:00

AWG for markers

This commit is contained in:
Stefan Monnier 2024-06-27 17:25:28 -04:00
parent f7725d85f3
commit f5700862ab
22 changed files with 736 additions and 406 deletions

View file

@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#endif
#include "lisp.h"
#include "marker.h"
#include "bignum.h"
#include "dispextern.h"
#include "intervals.h"
@ -4031,45 +4032,6 @@ build_overlay (bool front_advance, bool rear_advance,
return overlay;
}
DEFUN ("make-marker", Fmake_marker, Smake_marker, 0, 0, 0,
doc: /* Return a newly allocated marker which does not point at any place. */)
(void)
{
struct Lisp_Marker *p = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Marker,
PVEC_MARKER);
p->buffer = 0;
p->bytepos = 0;
p->charpos = 0;
p->next = NULL;
p->insertion_type = 0;
p->need_adjustment = 0;
return make_lisp_ptr (p, Lisp_Vectorlike);
}
/* Return a newly allocated marker which points into BUF
at character position CHARPOS and byte position BYTEPOS. */
Lisp_Object
build_marker (struct buffer *buf, ptrdiff_t charpos, ptrdiff_t bytepos)
{
/* No dead buffers here. */
eassert (BUFFER_LIVE_P (buf));
/* Every character is at least one byte. */
eassert (charpos <= bytepos);
struct Lisp_Marker *m = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Marker,
PVEC_MARKER);
m->buffer = buf;
m->charpos = charpos;
m->bytepos = bytepos;
m->insertion_type = 0;
m->need_adjustment = 0;
m->next = BUF_MARKERS (buf);
BUF_MARKERS (buf) = m;
return make_lisp_ptr (m, Lisp_Vectorlike);
}
/* Return a newly created vector or string with specified arguments as
elements. If all the arguments are characters that can fit
@ -7836,15 +7798,11 @@ sweep_symbols (void)
static void
unchain_dead_markers (struct buffer *buffer)
{
struct Lisp_Marker *this, **prev = &BUF_MARKERS (buffer);
while ((this = *prev))
if (vectorlike_marked_p (&this->header))
prev = &this->next;
else
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
if (!vectorlike_marked_p (&it.m->header))
{
this->buffer = NULL;
*prev = this->next;
markers_kill (it.t, it.m);
it.m->buffer = NULL;
}
}
@ -8287,7 +8245,6 @@ N should be nonnegative. */);
defsubr (&Smake_string);
defsubr (&Smake_bool_vector);
defsubr (&Smake_symbol);
defsubr (&Smake_marker);
defsubr (&Smake_finalizer);
defsubr (&Spurecopy);
defsubr (&Sgarbage_collect);

View file

@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <verify.h>
#include "lisp.h"
#include "marker.h"
#include "intervals.h"
#include "process.h"
#include "systime.h"
@ -660,8 +661,8 @@ even if it is dead. The return value is never nil. */)
reset_buffer (b);
reset_buffer_local_variables (b, 1);
BUF_ALL_MARKERS (b) = markers_new (4); /* Initial room for 4 markers. */
bset_mark (b, Fmake_marker ());
BUF_MARKERS (b) = NULL;
/* Put this in the alist of all live buffers. */
XSETBUFFER (buffer, b);
@ -2069,16 +2070,13 @@ cleaning up all windows currently displaying the buffer to be killed. */)
/* Unchain all markers that belong to this indirect buffer.
Don't unchain the markers that belong to the base buffer
or its other indirect buffers. */
struct Lisp_Marker **mp = &BUF_MARKERS (b);
while ((m = *mp))
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (b))
{
if (m->buffer == b)
if (it.m->buffer == b)
{
m->buffer = NULL;
*mp = m->next;
markers_kill (it.t, it.m);
it.m->buffer = NULL;
}
else
mp = &m->next;
}
/* Intervals should be owned by the base buffer (Bug#16502). */
i = buffer_intervals (b);
@ -2093,14 +2091,10 @@ cleaning up all windows currently displaying the buffer to be killed. */)
{
/* Unchain all markers of this buffer and its indirect buffers.
and leave them pointing nowhere. */
for (m = BUF_MARKERS (b); m; )
{
struct Lisp_Marker *next = m->next;
m->buffer = 0;
m->next = NULL;
m = next;
}
BUF_MARKERS (b) = NULL;
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (b))
it.m->buffer = NULL;
xfree (BUF_ALL_MARKERS (b));
BUF_ALL_MARKERS (b) = NULL;
set_buffer_intervals (b, NULL);
/* Perhaps we should explicitly free the interval tree here... */
@ -2624,21 +2618,20 @@ results, see Info node `(elisp)Swapping Text'. */)
other_buffer->text->end_unchanged = other_buffer->text->gpt;
swap_buffer_overlays (current_buffer, other_buffer);
{
struct Lisp_Marker *m;
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
if (m->buffer == other_buffer)
m->buffer = current_buffer;
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
if (it.m->buffer == other_buffer)
it.m->buffer = current_buffer;
else
/* Since there's no indirect buffer in sight, markers on
BUF_MARKERS(buf) should either be for `buf' or dead. */
eassert (!m->buffer);
for (m = BUF_MARKERS (other_buffer); m; m = m->next)
if (m->buffer == current_buffer)
m->buffer = other_buffer;
eassert (!it.m->buffer);
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (other_buffer))
if (it.m->buffer == current_buffer)
it.m->buffer = other_buffer;
else
/* Since there's no indirect buffer in sight, markers on
BUF_MARKERS(buf) should either be for `buf' or dead. */
eassert (!m->buffer);
eassert (!it.m->buffer);
}
{ /* Some of the C code expects that both window markers of a
live window points to that window's buffer. So since we
@ -2701,7 +2694,6 @@ If the multibyte flag was really changed, undo information of the
current buffer is cleared. */)
(Lisp_Object flag)
{
struct Lisp_Marker *tail, *markers;
Lisp_Object btail, other;
ptrdiff_t begv, zv;
bool narrowed = (BEG != BEGV || Z != ZV);
@ -2719,9 +2711,6 @@ current buffer is cleared. */)
instead. */
bset_undo_list (current_buffer, Qt);
/* If the cached position is for this buffer, clear it out. */
clear_charpos_cache (current_buffer);
if (NILP (flag))
begv = BEGV_BYTE, zv = ZV_BYTE;
else
@ -2750,9 +2739,8 @@ current buffer is cleared. */)
GPT = GPT_BYTE;
TEMP_SET_PT_BOTH (PT_BYTE, PT_BYTE);
for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
tail->charpos = tail->bytepos;
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
it.m->charpos = it.m->bytepos;
/* Convert multibyte form of 8-bit characters to unibyte. */
pos = BEG;
@ -2902,25 +2890,24 @@ current buffer is cleared. */)
TEMP_SET_PT_BOTH (position, byte);
}
tail = markers = BUF_MARKERS (current_buffer);
struct Lisp_Markers *t = BUF_ALL_MARKERS (current_buffer);
/* This prevents BYTE_TO_CHAR (that is, buf_bytepos_to_charpos) from
getting confused by the markers that have not yet been updated.
It is also a signal that it should never create a marker. */
BUF_MARKERS (current_buffer) = NULL;
BUF_ALL_MARKERS (current_buffer) = NULL;
for (; tail; tail = tail->next)
MARKERS_DO_ALL (it, t)
{
tail->bytepos = advance_to_char_boundary (tail->bytepos);
tail->charpos = BYTE_TO_CHAR (tail->bytepos);
it.m->bytepos = advance_to_char_boundary (it.m->bytepos);
it.m->charpos = BYTE_TO_CHAR (it.m->bytepos);
}
/* Make sure no markers were put on the chain
while the chain value was incorrect. */
if (BUF_MARKERS (current_buffer))
if (BUF_ALL_MARKERS (current_buffer))
emacs_abort ();
BUF_MARKERS (current_buffer) = markers;
BUF_ALL_MARKERS (current_buffer) = t;
/* Do this last, so it can calculate the new correspondences
between chars and bytes. */

View file

@ -25,6 +25,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "character.h"
#include "lisp.h"
#include "marker.h"
#include "itree.h"
INLINE_HEADER_BEGIN
@ -138,8 +139,8 @@ enum { BEG = 1, BEG_BYTE = BEG };
/* Compaction count. */
#define BUF_COMPACT(buf) ((buf)->text->compact)
/* Marker chain of buffer. */
#define BUF_MARKERS(buf) ((buf)->text->markers)
/* Table of all markers of buffer. */
#define BUF_ALL_MARKERS(buf) ((buf)->text->all_markers)
#define BUF_UNCHANGED_MODIFIED(buf) \
((buf)->text->unchanged_modified)
@ -271,14 +272,8 @@ struct buffer_text
/* Properties of this buffer's text. */
INTERVAL intervals;
/* The markers that refer to this buffer.
This is actually a single marker ---
successive elements in its marker `chain'
are the other markers referring to this buffer.
This is a singly linked unordered list, which means that it's
very cheap to add a marker to the list and it's also very cheap
to move a marker within a buffer. */
struct Lisp_Marker *markers;
/* The table of all the markers that refer to this buffer. */
struct Lisp_Markers *all_markers;
/* Usually false. Temporarily true in decode_coding_gap to
prevent Fgarbage_collect from shrinking the gap and losing

View file

@ -290,6 +290,7 @@ encode_coding_XXX (struct coding_system *coding)
#endif /* HAVE_WCHAR_H */
#include "lisp.h"
#include "marker.h"
#include "character.h"
#include "buffer.h"
#include "charset.h"
@ -8114,13 +8115,11 @@ decode_coding_object (struct coding_system *coding,
move_gap_both (from, from_byte);
if (BASE_EQ (src_object, dst_object))
{
struct Lisp_Marker *tail;
for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
{
tail->need_adjustment
= tail->charpos == (tail->insertion_type ? from : to);
need_marker_adjustment |= tail->need_adjustment;
it.m->need_adjustment
= it.m->charpos == (it.m->insertion_type ? from : to);
need_marker_adjustment |= it.m->need_adjustment;
}
saved_pt = PT, saved_pt_byte = PT_BYTE;
TEMP_SET_PT_BOTH (from, from_byte);
@ -8246,23 +8245,21 @@ decode_coding_object (struct coding_system *coding,
if (need_marker_adjustment)
{
struct Lisp_Marker *tail;
for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
if (tail->need_adjustment)
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
if (it.m->need_adjustment)
{
tail->need_adjustment = 0;
if (tail->insertion_type)
it.m->need_adjustment = 0;
if (it.m->insertion_type)
{
tail->bytepos = from_byte;
tail->charpos = from;
it.m->bytepos = from_byte;
it.m->charpos = from;
}
else
{
tail->bytepos = from_byte + coding->produced;
tail->charpos
it.m->bytepos = from_byte + coding->produced;
it.m->charpos
= (NILP (BVAR (current_buffer, enable_multibyte_characters))
? tail->bytepos : from + coding->produced_char);
? it.m->bytepos : from + coding->produced_char);
}
}
}
@ -8334,15 +8331,13 @@ encode_coding_object (struct coding_system *coding,
bool same_buffer = false;
if (BASE_EQ (src_object, dst_object) && BUFFERP (src_object))
{
struct Lisp_Marker *tail;
same_buffer = true;
for (tail = BUF_MARKERS (XBUFFER (src_object)); tail; tail = tail->next)
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (XBUFFER (src_object)))
{
tail->need_adjustment
= tail->charpos == (tail->insertion_type ? from : to);
need_marker_adjustment |= tail->need_adjustment;
it.m->need_adjustment
= it.m->charpos == (it.m->insertion_type ? from : to);
need_marker_adjustment |= it.m->need_adjustment;
}
}
@ -8501,23 +8496,21 @@ encode_coding_object (struct coding_system *coding,
if (need_marker_adjustment)
{
struct Lisp_Marker *tail;
for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
if (tail->need_adjustment)
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
if (it.m->need_adjustment)
{
tail->need_adjustment = 0;
if (tail->insertion_type)
it.m->need_adjustment = 0;
if (it.m->insertion_type)
{
tail->bytepos = from_byte;
tail->charpos = from;
it.m->bytepos = from_byte;
it.m->charpos = from;
}
else
{
tail->bytepos = from_byte + coding->produced;
tail->charpos
it.m->bytepos = from_byte + coding->produced;
it.m->charpos
= (NILP (BVAR (current_buffer, enable_multibyte_characters))
? tail->bytepos : from + coding->produced_char);
? it.m->bytepos : from + coding->produced_char);
}
}
}

View file

@ -38,6 +38,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#endif
#include "lisp.h"
#include "marker.h"
#include <float.h>
#include <limits.h>
@ -4384,7 +4385,6 @@ transpose_markers (ptrdiff_t start1, ptrdiff_t end1,
ptrdiff_t start2_byte, ptrdiff_t end2_byte)
{
register ptrdiff_t amt1, amt1_byte, amt2, amt2_byte, diff, diff_byte, mpos;
register struct Lisp_Marker *marker;
/* Update point as if it were a marker. */
if (PT < start1)
@ -4419,31 +4419,46 @@ transpose_markers (ptrdiff_t start1, ptrdiff_t end1,
amt1_byte = (end2_byte - start2_byte) + (start2_byte - end1_byte);
amt2_byte = (end1_byte - start1_byte) + (start2_byte - end1_byte);
for (marker = BUF_MARKERS (current_buffer); marker; marker = marker->next)
/* We move markers around here in a way that affects their ordering.
We can't do that from within the MARKERS_DO_ALL, so we use an auxiliary
table 't' to record markers we need to reinsert later. */
struct Lisp_Markers *t = BUF_ALL_MARKERS (current_buffer);
struct Lisp_Markers *taux = markers_new (4);
MARKERS_DO_ALL (it, t)
{
mpos = marker->bytepos;
mpos = it.m->bytepos;
if (mpos >= start1_byte && mpos < end2_byte)
{
markers_kill (it.t, it.m);
if (mpos < end1_byte)
mpos += amt1_byte;
else if (mpos < start2_byte)
mpos += diff_byte;
else
mpos -= amt2_byte;
marker->bytepos = mpos;
}
mpos = marker->charpos;
if (mpos >= start1 && mpos < end2)
{
it.m->bytepos = mpos;
mpos = it.m->charpos;
eassert (mpos >= start1 && mpos < end2);
if (mpos < end1)
mpos += amt1;
else if (mpos < start2)
mpos += diff;
else
mpos -= amt2;
it.m->charpos = mpos;
markers_add (taux, it.m);
}
marker->charpos = mpos;
}
MARKERS_DO_ALL (it, taux)
{
struct Lisp_Markers *tnew = markers_add (t, it.m);
/* There should always be enough space, so it should always
return the same table. */
eassert (tnew == t);
}
free (taux);
}
DEFUN ("transpose-regions", Ftranspose_regions, Stranspose_regions, 4, 5,

View file

@ -30,6 +30,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <math.h>
#include "lisp.h"
#include "marker.h"
#include "bignum.h"
#include "character.h"
#include "coding.h"

View file

@ -20,6 +20,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
#include "lisp.h"
#include "marker.h"
#include "character.h"
#include "buffer.h"
#include "category.h"

View file

@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <intprops.h>
#include "lisp.h"
#include "marker.h"
#include "composite.h"
#include "intervals.h"
#include "character.h"
@ -228,11 +229,12 @@ adjust_suspend_auto_hscroll (ptrdiff_t from, ptrdiff_t to)
if (WINDOWP (selected_window))
{
struct window *w = XWINDOW (selected_window);
ptrdiff_t old_point;
if (BUFFERP (w->contents)
&& XBUFFER (w->contents) == current_buffer
&& XMARKER (w->old_pointm)->charpos >= from
&& XMARKER (w->old_pointm)->charpos <= to)
&& (old_point = marker_position (w->old_pointm),
from <= old_point && old_point <= to))
w->suspend_auto_hscroll = 0;
}
}
@ -249,29 +251,9 @@ void
adjust_markers_for_delete (ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t to, ptrdiff_t to_byte)
{
struct Lisp_Marker *m;
ptrdiff_t charpos;
adjust_suspend_auto_hscroll (from, to);
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
{
charpos = m->charpos;
eassert (charpos <= Z);
/* If the marker is after the deletion,
relocate by number of chars / bytes deleted. */
if (charpos > to)
{
m->charpos -= to - from;
m->bytepos -= to_byte - from_byte;
}
/* Here's the case where a marker is inside text being deleted. */
else if (charpos > from)
{
m->charpos = from;
m->bytepos = from_byte;
}
}
markers_adjust_for_delete (BUF_ALL_MARKERS (current_buffer),
from, from_byte, to, to_byte);
adjust_overlays_for_delete (from, to - from);
}
@ -288,30 +270,9 @@ void
adjust_markers_for_insert (ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t to, ptrdiff_t to_byte, bool before_markers)
{
struct Lisp_Marker *m;
ptrdiff_t nchars = to - from;
ptrdiff_t nbytes = to_byte - from_byte;
adjust_suspend_auto_hscroll (from, to);
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
{
eassert (m->bytepos >= m->charpos
&& m->bytepos - m->charpos <= Z_BYTE - Z);
if (m->bytepos == from_byte)
{
if (m->insertion_type || before_markers)
{
m->bytepos = to_byte;
m->charpos = to;
}
}
else if (m->bytepos > from_byte)
{
m->bytepos += nbytes;
m->charpos += nchars;
}
}
markers_adjust_for_insert (BUF_ALL_MARKERS (current_buffer),
from, from_byte, to, to_byte, before_markers);
adjust_overlays_for_insert (from, to - from, before_markers);
}
@ -343,31 +304,11 @@ adjust_markers_for_replace (ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t old_chars, ptrdiff_t old_bytes,
ptrdiff_t new_chars, ptrdiff_t new_bytes)
{
register struct Lisp_Marker *m;
ptrdiff_t prev_to_byte = from_byte + old_bytes;
ptrdiff_t diff_chars = new_chars - old_chars;
ptrdiff_t diff_bytes = new_bytes - old_bytes;
adjust_suspend_auto_hscroll (from, from + old_chars);
/* FIXME: When OLD_CHARS is 0, this "replacement" is really just an
insertion, but the behavior we provide here in that case is that of
`insert-before-markers` rather than that of `insert`.
Maybe not a bug, but not a feature either. */
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
{
if (m->bytepos >= prev_to_byte)
{
m->charpos += diff_chars;
m->bytepos += diff_bytes;
}
else if (m->bytepos > from_byte)
{
m->charpos = from;
m->bytepos = from_byte;
}
}
markers_adjust_for_replace (BUF_ALL_MARKERS (current_buffer),
from, from_byte, old_chars, old_bytes,
new_chars, new_bytes);
check_markers ();
adjust_overlays_for_insert (from + old_chars, new_chars, true);
@ -415,36 +356,33 @@ adjust_markers_bytepos (ptrdiff_t from, ptrdiff_t from_byte,
{
/* Make sure each affected marker's bytepos is equal to
its charpos. */
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
{
if (m->bytepos > from_byte
&& (to_z || m->bytepos <= to_byte))
m->bytepos = m->charpos;
if (it.m->bytepos > from_byte
&& (to_z || it.m->bytepos <= to_byte))
it.m->bytepos = it.m->charpos;
}
}
else
{
for (m = BUF_MARKERS (current_buffer); m; m = m->next)
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
{
/* Recompute each affected marker's bytepos. */
if (m->bytepos > from_byte
&& (to_z || m->bytepos <= to_byte))
if (it.m->bytepos > from_byte
&& (to_z || it.m->bytepos <= to_byte))
{
if (m->charpos < beg
&& beg - m->charpos > m->charpos - from)
if (it.m->charpos < beg
&& beg - it.m->charpos > it.m->charpos - from)
{
beg = from;
begbyte = from_byte;
}
m->bytepos = count_bytes (beg, begbyte, m->charpos);
beg = m->charpos;
begbyte = m->bytepos;
it.m->bytepos = count_bytes (beg, begbyte, it.m->charpos);
beg = it.m->charpos;
begbyte = it.m->bytepos;
}
}
}
/* Make sure cached charpos/bytepos is invalid. */
clear_charpos_cache (current_buffer);
}

View file

@ -42,6 +42,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <intprops.h>
#include "lisp.h"
#include "marker.h"
#include "intervals.h"
#include "buffer.h"
#include "puresize.h"

View file

@ -23,6 +23,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <sys/stat.h>
#include "lisp.h"
#include "marker.h"
#include "coding.h"
#include "termchar.h"
#include "termopts.h"

View file

@ -2828,50 +2828,6 @@ knuth_hash (hash_hash_t hash, unsigned bits)
return wide_product >> (32 - bits);
}
struct Lisp_Marker
{
union vectorlike_header header;
/* This is the buffer that the marker points into, or 0 if it points nowhere.
Note: a chain of markers can contain markers pointing into different
buffers (the chain is per buffer_text rather than per buffer, so it's
shared between indirect buffers). */
/* This is used for (other than NULL-checking):
- Fmarker_buffer
- Fset_marker: check eq(oldbuf, newbuf) to avoid unchain+rechain.
- unchain_marker: to find the list from which to unchain.
- Fkill_buffer: to only unchain the markers of current indirect buffer.
*/
struct buffer *buffer;
/* This flag is temporarily used in the functions
decode/encode_coding_object to record that the marker position
must be adjusted after the conversion. */
bool_bf need_adjustment : 1;
/* True means normal insertion at the marker's position
leaves the marker after the inserted text. */
bool_bf insertion_type : 1;
/* The remaining fields are meaningless in a marker that
does not point anywhere. */
/* For markers that point somewhere,
this is used to chain of all the markers in a given buffer.
The chain does not preserve markers from garbage collection;
instead, markers are removed from the chain when freed by GC. */
/* We could remove it and use an array in buffer_text instead.
That would also allow us to preserve it ordered. */
struct Lisp_Marker *next;
/* This is the char position where the marker points. */
ptrdiff_t charpos;
/* This is the byte position.
It's mostly used as a charpos<->bytepos cache (i.e. it's not directly
used to implement the functionality of markers, but rather to (ab)use
markers as a cache for char<->byte mappings). */
ptrdiff_t bytepos;
} GCALIGNED_STRUCT;
struct Lisp_Overlay
/* An overlay's real data content is:
- plist
@ -5000,22 +4956,6 @@ extern void init_buffer_once (void);
extern void init_buffer (void);
extern void syms_of_buffer (void);
/* Defined in marker.c. */
extern ptrdiff_t marker_position (Lisp_Object);
extern ptrdiff_t marker_byte_position (Lisp_Object);
extern void clear_charpos_cache (struct buffer *);
extern ptrdiff_t buf_charpos_to_bytepos (struct buffer *, ptrdiff_t);
extern ptrdiff_t buf_bytepos_to_charpos (struct buffer *, ptrdiff_t);
extern void detach_marker (Lisp_Object);
extern void unchain_marker (struct Lisp_Marker *);
extern Lisp_Object set_marker_restricted (Lisp_Object, Lisp_Object, Lisp_Object);
extern Lisp_Object set_marker_both (Lisp_Object, Lisp_Object, ptrdiff_t, ptrdiff_t);
extern Lisp_Object set_marker_restricted_both (Lisp_Object, Lisp_Object,
ptrdiff_t, ptrdiff_t);
extern Lisp_Object build_marker (struct buffer *, ptrdiff_t, ptrdiff_t);
extern void syms_of_marker (void);
/* Defined in fileio.c. */
extern Lisp_Object file_name_directory (Lisp_Object);

View file

@ -32,6 +32,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <math.h>
#include <stat-time.h>
#include "lisp.h"
#include "marker.h"
#include "dispextern.h"
#include "intervals.h"
#include "character.h"

View file

@ -26,9 +26,52 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#endif
#include "lisp.h"
#include "marker.h"
#include "character.h"
#include "buffer.h"
#include "window.h"
#include "stdlib.h"
#ifdef ABSTRACT_LISP_MARKER
struct Lisp_Marker
{
union vectorlike_header header;
/* This is the buffer that the marker points into, or 0 if it points nowhere.
Note: a chain of markers can contain markers pointing into different
buffers (the chain is per buffer_text rather than per buffer, so it's
shared between indirect buffers). */
/* This is used for (other than NULL-checking):
- Fmarker_buffer
- Fset_marker: check eq(oldbuf, newbuf) to avoid unchain+rechain.
- unchain_marker: to find the list from which to unchain.
- Fkill_buffer: to only unchain the markers of current indirect buffer.
*/
struct buffer *buffer;
/* This flag is temporarily used in the functions
decode/encode_coding_object to record that the marker position
must be adjusted after the conversion. */
bool_bf need_adjustment : 1;
/* True means normal insertion at the marker's position
leaves the marker after the inserted text. */
bool_bf insertion_type : 1;
/* True means that this is a chars<->bytes conversion cache entry
rather than a true marker. */
bool_bf cache : 1;
/* The remaining fields are meaningless in a marker that
does not point anywhere. */
/* This is the char position where the marker points. */
ptrdiff_t charpos;
/* This is the byte position.
It's mostly used as a charpos<->bytepos cache (i.e. it's not directly
used to implement the functionality of markers, but rather to (ab)use
markers as a cache for char<->byte mappings). */
ptrdiff_t bytepos;
} GCALIGNED_STRUCT;
#endif
/* Record one cached position found recently by
buf_charpos_to_bytepos or buf_bytepos_to_charpos. */
@ -38,6 +81,306 @@ static ptrdiff_t cached_bytepos;
static struct buffer *cached_buffer;
static modiff_count cached_modiff;
/*****************************************************************/
/* Set of markers represented as a sorted array-with-gap (AWG). */
/*****************************************************************/
typedef unsigned int m_index_t;
struct Lisp_Markers
{
m_index_t size;
m_index_t gap_beg;
m_index_t gap_end;
struct Lisp_Marker *markers[FLEXIBLE_ARRAY_MEMBER];
};
static void
markers_sanity_check (struct Lisp_Markers *t)
{
eassert (t->markers);
eassert (t->size > 0);
eassert (t->gap_beg >= 0);
eassert (t->gap_beg <= t->gap_end);
eassert (t->gap_end <= t->size);
m_index_t i;
ptrdiff_t lastpos = BEG;
for (i = 0; i < t->size; i++)
{
if (i == t->gap_beg)
{
for (; i < t->gap_end; i++)
eassert (t->markers[i] == NULL);
if (i == t->size)
break;
}
eassert (t->markers[i]->buffer);
/* eassert (BUF_ALL_MARKERS (t->markers[i]->buffer) == t); */
eassert (t->markers[i]->charpos >= lastpos);
lastpos = t->markers[i]->charpos;
}
}
#define DEFINE_SEARCH_FUN(funname, thepos) \
static m_index_t \
funname (struct Lisp_Markers *t, ptrdiff_t thepos) \
{ \
m_index_t bot, top; \
markers_sanity_check (t); \
/* See whether to search before or after the gap. */ \
if (t->gap_beg >= 1 && t->markers[t->gap_beg - 1]->thepos >= thepos) \
(bot = 0, top = t->gap_beg); \
else \
(bot = t->gap_end, top = t->size); \
\
/* Binary search. */ \
while (bot < top) \
{ \
m_index_t mid = bot + ((top - bot) / 2); \
eassert (mid < top); \
if (t->markers[mid]->thepos >= thepos) \
top = mid; \
else \
bot = mid + 1; \
} \
eassert (bot == top); \
eassert (bot == t->size || t->markers[bot]->thepos >= thepos); \
eassert ((bot == t->gap_end ? t->gap_beg : bot) == 0 \
|| t->markers[(bot == t->gap_end ? t->gap_beg : bot)-1]->thepos \
< thepos); \
return bot; \
}
/* Return the best approximation of the index in the AWG
corresponding to a given charpos or bytepos. */
DEFINE_SEARCH_FUN (markers_search_charpos, charpos);
DEFINE_SEARCH_FUN (markers_search_bytepos, bytepos);
/* Return the index in the AWG where the marker M is found.
This function presumes that M is indeed in the AWG. */
static m_index_t
markers_search_marker (struct Lisp_Markers *t, struct Lisp_Marker *m)
{
m_index_t beg = markers_search_charpos (t, m->charpos);
eassert (t->markers[beg]->charpos == m->charpos);
while (t->markers[beg] != m)
{
beg = (beg == t->gap_beg - 1) ? t->gap_end : beg + 1;
eassert (t->markers[beg]->charpos == m->charpos);
}
return beg;
}
static void
markers_move_gap (struct Lisp_Markers *t, m_index_t new_beg)
{
m_index_t old_beg = t->gap_beg;
m_index_t old_end = t->gap_end;
eassert (new_beg <= t->gap_beg || new_beg >= t->gap_end);
if (new_beg > old_beg)
new_beg = old_beg + (new_beg - old_end);
if (old_beg == new_beg)
return;
else if (new_beg > old_beg)
memmove (&t->markers[old_beg], &t->markers[old_end],
(new_beg - old_beg) * sizeof (struct Lisp_Marker *));
else
{
m_index_t size = old_beg - new_beg;
memmove (&t->markers[old_end - size], &t->markers[new_beg],
size * sizeof (struct Lisp_Marker *));
}
t->gap_beg = new_beg;
t->gap_end = new_beg + (old_end - old_beg);
/* FIME: Get rid of it? */
memset (&t->markers[new_beg], 0,
(old_end - old_beg) * sizeof (struct Lisp_Marker *));
markers_sanity_check (t);
}
static void
markers_move_gap_to_charpos (struct Lisp_Markers *t, ptrdiff_t charpos)
{
m_index_t i = markers_search_charpos (t, charpos);
markers_move_gap (t, i);
eassert (t->gap_beg == 0
|| t->markers[t->gap_beg - 1]->charpos < charpos);
eassert (t->gap_end == t->size
|| t->markers[t->gap_end]->charpos >= charpos);
}
void
markers_kill (struct Lisp_Markers *t, struct Lisp_Marker *m)
{
m_index_t i = markers_search_marker (t, m);
if (i < t->gap_beg)
{
markers_move_gap (t, i + 1);
eassert (t->gap_beg == i + 1);
t->gap_beg = i;
}
else
{
markers_move_gap (t, i);
eassert (t->gap_end == i);
t->gap_end = i + 1;
}
eassert (t->markers[i] == m);
t->markers[i] = NULL;
markers_sanity_check (t);
}
static struct Lisp_Markers *
markers_grow (struct Lisp_Markers *t)
{
eassert (t->gap_beg == t->gap_end);
m_index_t oldsize = t->size;
m_index_t increment = max (2, oldsize / 2);
m_index_t newsize = oldsize + increment;
if (newsize < oldsize) /* Overflow! */
/* This can happen only if you have ~4G markers, in which case the
algorithmic complexity of the code in this file should make Emacs
unusable anyway. */
error ("Table of markers full!");
struct Lisp_Marker **oldmarkers = t->markers;
struct Lisp_Markers *newt
= xmalloc (sizeof (struct Lisp_Markers)
+ newsize * sizeof (struct Lisp_Marker *));
struct Lisp_Marker **newmarkers = newt->markers;
memcpy (newmarkers, oldmarkers, oldsize * sizeof (struct Lisp_Marker *));
memset (&newmarkers[oldsize], 0, increment * sizeof (struct Lisp_Marker *));
xfree (t);
newt->size = newsize;
newt->gap_beg = oldsize;
newt->gap_end = newsize;
markers_sanity_check (newt);
return newt;
}
struct Lisp_Markers *
markers_add (struct Lisp_Markers *t, struct Lisp_Marker *m)
{
if (t->gap_beg == t->gap_end)
t = markers_grow (t);
markers_move_gap_to_charpos (t, m->charpos);
t->markers[t->gap_beg++] = m;
markers_sanity_check (t);
return t;
}
void
markers_adjust_for_insert (struct Lisp_Markers *t,
ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t to, ptrdiff_t to_byte,
bool before_markers)
{
markers_move_gap_to_charpos (t, from);
m_index_t i = t->gap_end;
m_index_t size = t->size;
ptrdiff_t charoffset = to - from;
ptrdiff_t byteoffset = to_byte - from_byte;
/* FIXME: This can mess up ordering because we move some but not
all markers at 'from'. */
for (; i < size && t->markers[i]->charpos == from; i++)
if (t->markers[i]->insertion_type)
{
t->markers[i]->charpos += charoffset;
t->markers[i]->bytepos += byteoffset;
}
for (; i < size; i++)
{
t->markers[i]->charpos += charoffset;
t->markers[i]->bytepos += byteoffset;
}
markers_sanity_check (t);
}
void
markers_adjust_for_replace (struct Lisp_Markers *t,
ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t old_chars, ptrdiff_t old_bytes,
ptrdiff_t new_chars, ptrdiff_t new_bytes)
{
eassert (old_chars > 0);
markers_move_gap_to_charpos (t, from);
m_index_t i = t->gap_end;
m_index_t size = t->size;
ptrdiff_t prev_to = from + old_chars;
for (; i < size && t->markers[i]->charpos < prev_to; i++)
{
t->markers[i]->charpos = from;
t->markers[i]->bytepos = from_byte;
}
ptrdiff_t charoffset = new_chars - old_chars;
ptrdiff_t byteoffset = new_bytes - old_bytes;
for (; i < size; i++)
{
t->markers[i]->charpos += charoffset;
t->markers[i]->bytepos += byteoffset;
}
markers_sanity_check (t);
}
void
markers_adjust_for_delete (struct Lisp_Markers *t,
ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t to, ptrdiff_t to_byte)
{
eassert (to > from);
markers_adjust_for_replace (t, from, from_byte,
to - from, to_byte - from_byte,
0, 0);
}
bool
markers_full_p (struct Lisp_Markers *t)
{
return t->gap_beg == t->gap_end;
}
struct Lisp_Markers *
markers_new (unsigned int size)
{
struct Lisp_Markers *t = xmalloc (sizeof (struct Lisp_Markers)
+ size * sizeof (struct Lisp_Marker *));
t->size = size;
t->gap_beg = 0;
t->gap_end = size;
memset (t->markers, 0, size * sizeof (struct Lisp_Marker *));
return t;
}
struct markers__iterator
{
struct Lisp_Markers *t;
m_index_t i;
ptrdiff_t to;
};
struct markers_iterator
markers_iterator_all (struct Lisp_Markers *t)
{
struct markers_iterator it = { .t = t, .i = t->gap_beg ? 0 : t->gap_end };
it.m = it.i < t->size ? t->markers[it.i] : NULL;
return it;
}
void
markers_iterator_next (struct markers_iterator *it)
{
it->i++;
if (it->i == it->t->gap_beg)
it->i = it->t->gap_end;
it->m = it->i < it->t->size ? it->t->markers[it->i] : NULL;
}
/***********************************************************/
/* Older set of markers as an unsorted linked list */
/***********************************************************/
/* Juanma Barranquero <lekktu@gmail.com> reported ~3x increased
bootstrap time when byte_char_debug_check is enabled; so this
is never turned on by --enable-checking configure option. */
@ -139,6 +482,13 @@ CHECK_MARKER (Lisp_Object x)
CHECK_TYPE (MARKERP (x), Qmarkerp, x);
}
static void
cache_bytechar (struct buffer *buf, ptrdiff_t charpos, ptrdiff_t bytepos)
{
Lisp_Object m = build_marker (buf, charpos, bytepos);
XMARKER (m)->cache = true;
}
/* When converting bytes from/to chars, we look through the list of
markers to try and find a good starting point (since markers keep
track of both bytepos and charpos at the same time).
@ -159,14 +509,12 @@ CHECK_MARKER (Lisp_Object x)
The asymptotic behavior is still poor, tho, so in largish buffers with many
overlays (e.g. 300KB and 30K overlays), it can still be a bottleneck. */
#define BYTECHAR_DISTANCE_INITIAL 50
#define BYTECHAR_DISTANCE_INCREMENT 50
/* Return the byte position corresponding to CHARPOS in B. */
ptrdiff_t
buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
{
struct Lisp_Marker *tail;
ptrdiff_t best_above, best_above_byte;
ptrdiff_t best_below, best_below_byte;
ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
@ -202,16 +550,24 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
if (b == cached_buffer && BUF_MODIFF (b) == cached_modiff)
CONSIDER (cached_charpos, cached_bytepos);
for (tail = BUF_MARKERS (b);
/* If we are down to a range of DISTANCE chars,
don't bother checking any other markers;
scan the intervening chars directly now. */
tail && !(best_above - charpos < distance
|| charpos - best_below < distance);
tail = tail->next,
distance += BYTECHAR_DISTANCE_INCREMENT)
CONSIDER (tail->charpos, tail->bytepos);
if (!(best_above - charpos < distance
|| charpos - best_below < distance))
{
struct Lisp_Markers *t = BUF_ALL_MARKERS (b);
m_index_t i = markers_search_charpos (t, charpos);
if (i < t->size)
{
struct Lisp_Marker *m = t->markers[i];
CONSIDER (m->charpos, m->bytepos);
}
if (i == t->gap_end)
i = t->gap_beg;
if (i > 0)
{
struct Lisp_Marker *m = t->markers[i - 1];
CONSIDER (m->charpos, m->bytepos);
}
}
/* We get here if we did not exactly hit one of the known places.
We have one known above and one known below.
Scan, counting characters, from whichever one is closer. */
@ -231,7 +587,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
cache the correspondence by creating a marker here.
It will last until the next GC. */
if (record)
build_marker (b, best_below, best_below_byte);
cache_bytechar (b, best_below, best_below_byte);
byte_char_debug_check (b, best_below, best_below_byte);
@ -240,6 +596,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
cached_charpos = best_below;
cached_bytepos = best_below_byte;
eassert (best_below_byte >= charpos);
return best_below_byte;
}
else
@ -256,7 +613,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
cache the correspondence by creating a marker here.
It will last until the next GC. */
if (record)
build_marker (b, best_above, best_above_byte);
cache_bytechar (b, best_above, best_above_byte);
byte_char_debug_check (b, best_above, best_above_byte);
@ -265,6 +622,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
cached_charpos = best_above;
cached_bytepos = best_above_byte;
eassert (best_above_byte >= charpos);
return best_above_byte;
}
}
@ -319,7 +677,6 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
ptrdiff_t
buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
{
struct Lisp_Marker *tail;
ptrdiff_t best_above, best_above_byte;
ptrdiff_t best_below, best_below_byte;
ptrdiff_t distance = BYTECHAR_DISTANCE_INITIAL;
@ -350,15 +707,24 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
if (b == cached_buffer && BUF_MODIFF (b) == cached_modiff)
CONSIDER (cached_bytepos, cached_charpos);
for (tail = BUF_MARKERS (b);
/* If we are down to a range of DISTANCE bytes,
don't bother checking any other markers;
scan the intervening chars directly now. */
tail && !(best_above_byte - bytepos < distance
|| bytepos - best_below_byte < distance);
tail = tail->next,
distance += BYTECHAR_DISTANCE_INCREMENT)
CONSIDER (tail->bytepos, tail->charpos);
if (!(best_above_byte - bytepos < distance
|| bytepos - best_below_byte < distance))
{
struct Lisp_Markers *t = BUF_ALL_MARKERS (b);
m_index_t i = markers_search_bytepos (t, bytepos);
if (i < t->size)
{
struct Lisp_Marker *m = t->markers[i];
CONSIDER (m->bytepos, m->charpos);
}
if (i == t->gap_end)
i = t->gap_beg;
if (i > 0)
{
struct Lisp_Marker *m = t->markers[i - 1];
CONSIDER (m->bytepos, m->charpos);
}
}
/* We get here if we did not exactly hit one of the known places.
We have one known above and one known below.
@ -379,8 +745,8 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
It will last until the next GC.
But don't do it if BUF_MARKERS is nil;
that is a signal from Fset_buffer_multibyte. */
if (record && BUF_MARKERS (b))
build_marker (b, best_below, best_below_byte);
if (record && BUF_ALL_MARKERS (b))
cache_bytechar (b, best_below, best_below_byte);
byte_char_debug_check (b, best_below, best_below_byte);
@ -389,6 +755,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
cached_charpos = best_below;
cached_bytepos = best_below_byte;
eassert (best_below <= bytepos);
return best_below;
}
else
@ -406,8 +773,8 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
It will last until the next GC.
But don't do it if BUF_MARKERS is nil;
that is a signal from Fset_buffer_multibyte. */
if (record && BUF_MARKERS (b))
build_marker (b, best_above, best_above_byte);
if (record && BUF_ALL_MARKERS (b))
cache_bytechar (b, best_above, best_above_byte);
byte_char_debug_check (b, best_above, best_above_byte);
@ -416,6 +783,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
cached_charpos = best_above;
cached_bytepos = best_above_byte;
eassert (best_above <= bytepos);
return best_above;
}
}
@ -480,16 +848,14 @@ attach_marker (struct Lisp_Marker *m, struct buffer *b,
else
eassert (charpos <= bytepos);
if (m->buffer)
markers_kill (BUF_ALL_MARKERS (m->buffer), m);
m->charpos = charpos;
m->bytepos = bytepos;
m->buffer = b;
if (m->buffer != b)
{
unchain_marker (m);
m->buffer = b;
m->next = BUF_MARKERS (b);
BUF_MARKERS (b) = m;
}
BUF_ALL_MARKERS (b) = markers_add (BUF_ALL_MARKERS (b), m);
}
/* If BUFFER is nil, return current buffer pointer. Next, check
@ -525,55 +891,19 @@ set_marker_internal (Lisp_Object marker, Lisp_Object position,
/* Optimize the special case where we are copying the position of
an existing marker, and MARKER is already in the same buffer. */
else if (MARKERP (position) && b == XMARKER (position)->buffer
&& b == m->buffer)
else if (MARKERP (position) && b == XMARKER (position)->buffer)
{
m->bytepos = XMARKER (position)->bytepos;
m->charpos = XMARKER (position)->charpos;
attach_marker (m, b, XMARKER (position)->charpos,
XMARKER (position)->bytepos);
}
else
{
register ptrdiff_t charpos, bytepos;
/* Do not use CHECK_FIXNUM_COERCE_MARKER because we
don't want to call buf_charpos_to_bytepos if POSITION
is a marker and so we know the bytepos already. */
if (FIXNUMP (position))
{
#if EMACS_INT_MAX > PTRDIFF_MAX
/* A --with-wide-int build. */
EMACS_INT cpos = XFIXNUM (position);
if (cpos > PTRDIFF_MAX)
cpos = PTRDIFF_MAX;
charpos = cpos;
bytepos = -1;
#else
charpos = XFIXNUM (position), bytepos = -1;
#endif
}
else if (MARKERP (position))
{
charpos = XMARKER (position)->charpos;
bytepos = XMARKER (position)->bytepos;
}
else
wrong_type_argument (Qinteger_or_marker_p, position);
ptrdiff_t charpos = fix_position (position);
charpos = clip_to_bounds
(restricted ? BUF_BEGV (b) : BUF_BEG (b), charpos,
restricted ? BUF_ZV (b) : BUF_Z (b));
/* Don't believe BYTEPOS if it comes from a different buffer,
since that buffer might have a very different correspondence
between character and byte positions. */
if (bytepos == -1
|| !(MARKERP (position) && XMARKER (position)->buffer == b))
bytepos = buf_charpos_to_bytepos (b, charpos);
else
bytepos = clip_to_bounds
(restricted ? BUF_BEGV_BYTE (b) : BUF_BEG_BYTE (b),
bytepos, restricted ? BUF_ZV_BYTE (b) : BUF_Z_BYTE (b));
ptrdiff_t bytepos = buf_charpos_to_bytepos (b, charpos);
attach_marker (m, b, charpos, bytepos);
}
@ -674,6 +1004,7 @@ set_marker_restricted_both (Lisp_Object marker, Lisp_Object buffer,
void
detach_marker (Lisp_Object marker)
{
/* FIXME: Roundabout way to call `unchain_marker`. */
Fset_marker (marker, Qnil, Qnil);
}
@ -687,34 +1018,11 @@ unchain_marker (register struct Lisp_Marker *marker)
if (b)
{
register struct Lisp_Marker *tail, **prev;
/* No dead buffers here. */
eassert (BUFFER_LIVE_P (b));
markers_kill (BUF_ALL_MARKERS (b), marker);
marker->buffer = NULL;
prev = &BUF_MARKERS (b);
for (tail = BUF_MARKERS (b); tail; prev = &tail->next, tail = *prev)
if (marker == tail)
{
if (*prev == BUF_MARKERS (b))
{
/* Deleting first marker from the buffer's chain. Crash
if new first marker in chain does not say it belongs
to the same buffer, or at least that they have the same
base buffer. */
if (tail->next && b->text != tail->next->buffer->text)
emacs_abort ();
}
*prev = tail->next;
/* We have removed the marker from the chain;
no need to scan the rest of the chain. */
break;
}
/* Error if marker was not in it's chain. */
eassert (tail != NULL);
}
}
@ -794,6 +1102,45 @@ If TYPE is nil, it means the marker stays behind when you insert text at it. */
return type;
}
DEFUN ("make-marker", Fmake_marker, Smake_marker, 0, 0, 0,
doc: /* Return a newly allocated marker which does not point at any place. */)
(void)
{
struct Lisp_Marker *p = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Marker,
PVEC_MARKER);
p->buffer = 0;
p->bytepos = 0;
p->charpos = 0;
p->insertion_type = false;
p->need_adjustment = false;
p->cache = false;
return make_lisp_ptr (p, Lisp_Vectorlike);
}
/* Return a newly allocated marker which points into BUF
at character position CHARPOS and byte position BYTEPOS. */
Lisp_Object
build_marker (struct buffer *buf, ptrdiff_t charpos, ptrdiff_t bytepos)
{
/* No dead buffers here. */
eassert (BUFFER_LIVE_P (buf));
/* Every character is at least one byte. */
eassert (charpos <= bytepos);
struct Lisp_Marker *m = ALLOCATE_PLAIN_PSEUDOVECTOR (struct Lisp_Marker,
PVEC_MARKER);
m->buffer = buf;
m->charpos = charpos;
m->bytepos = bytepos;
m->insertion_type = false;
m->need_adjustment = false;
m->cache = false;
BUF_ALL_MARKERS (buf) = markers_add (BUF_ALL_MARKERS (buf), m);
return make_lisp_ptr (m, Lisp_Vectorlike);
}
#ifdef MARKER_DEBUG
/* For debugging -- count the markers in buffer BUF. */
@ -801,13 +1148,8 @@ If TYPE is nil, it means the marker stays behind when you insert text at it. */
int
count_markers (struct buffer *buf)
{
int total = 0;
struct Lisp_Marker *tail;
for (tail = BUF_MARKERS (buf); tail; tail = tail->next)
total++;
return total;
struct Lisp_Markers *t = BUF_ALL_MARKERS (current_buffer);
return t->size - (t->gap_end - t->gap_beg);
}
/* For debugging -- recompute the bytepos corresponding
@ -833,6 +1175,7 @@ verify_bytepos (ptrdiff_t charpos)
void
syms_of_marker (void)
{
defsubr (&Smake_marker);
defsubr (&Smarker_position);
defsubr (&Smarker_last_position);
defsubr (&Smarker_buffer);

105
src/marker.h Normal file
View file

@ -0,0 +1,105 @@
#ifndef EMACS_MARKER_H
#define EMACS_MARKER_H
#include "lisp.h"
#ifndef ABSTRACT_LISP_MARKER
struct Lisp_Marker
{
union vectorlike_header header;
/* This is the buffer that the marker points into, or 0 if it points nowhere.
Note: a chain of markers can contain markers pointing into different
buffers (the chain is per buffer_text rather than per buffer, so it's
shared between indirect buffers). */
/* This is used for (other than NULL-checking):
- Fmarker_buffer
- Fset_marker: check eq(oldbuf, newbuf) to avoid unchain+rechain.
- unchain_marker: to find the list from which to unchain.
- Fkill_buffer: to only unchain the markers of current indirect buffer.
*/
struct buffer *buffer;
/* This flag is temporarily used in the functions
decode/encode_coding_object to record that the marker position
must be adjusted after the conversion. */
bool_bf need_adjustment : 1;
/* True means normal insertion at the marker's position
leaves the marker after the inserted text. */
bool_bf insertion_type : 1;
/* True means that this is a chars<->bytes conversion cache entry
rather than a true marker. */
bool_bf cache : 1;
/* The remaining fields are meaningless in a marker that
does not point anywhere. */
/* This is the char position where the marker points. */
ptrdiff_t charpos;
/* This is the byte position.
It's mostly used as a charpos<->bytepos cache (i.e. it's not directly
used to implement the functionality of markers, but rather to (ab)use
markers as a cache for char<->byte mappings). */
ptrdiff_t bytepos;
} GCALIGNED_STRUCT;
#endif
struct Lisp_Markers;
extern bool markers_full_p (struct Lisp_Markers *t);
extern struct Lisp_Markers *markers_new (unsigned int size);
extern void markers_kill (struct Lisp_Markers *t, struct Lisp_Marker *m);
extern struct Lisp_Markers *markers_add (struct Lisp_Markers *t,
struct Lisp_Marker *m);
extern void markers_adjust_for_insert (struct Lisp_Markers *t,
ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t to, ptrdiff_t to_byte,
bool before_markers);
extern void markers_adjust_for_replace
(struct Lisp_Markers *t,
ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t old_chars, ptrdiff_t old_bytes,
ptrdiff_t new_chars, ptrdiff_t new_bytes);
extern void markers_adjust_for_delete (struct Lisp_Markers *t,
ptrdiff_t from, ptrdiff_t from_byte,
ptrdiff_t to, ptrdiff_t to_byte);
struct markers_iterator
{
struct Lisp_Markers *t;
struct Lisp_Marker *m;
unsigned int i;
};
extern struct markers_iterator markers_iterator_all (struct Lisp_Markers *t);
extern void markers_iterator_next (struct markers_iterator *it);
#define MARKERS_DO_ALL(it, t) \
for (struct markers_iterator it = markers_iterator_all (t); \
it.m; markers_iterator_next (&it))
/**********************************************************************/
extern void clear_charpos_cache (struct buffer *b);
extern ptrdiff_t buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos);
extern ptrdiff_t buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos);
extern Lisp_Object set_marker_restricted (Lisp_Object marker,
Lisp_Object position,
Lisp_Object buffer);
extern Lisp_Object set_marker_both (Lisp_Object marker, Lisp_Object buffer,
ptrdiff_t charpos, ptrdiff_t bytepos);
extern Lisp_Object set_marker_restricted_both (Lisp_Object marker,
Lisp_Object buffer,
ptrdiff_t charpos,
ptrdiff_t bytepos);
extern void detach_marker (Lisp_Object marker);
extern void unchain_marker (register struct Lisp_Marker *marker);
extern ptrdiff_t marker_position (Lisp_Object marker);
extern ptrdiff_t marker_byte_position (Lisp_Object marker);
extern Lisp_Object build_marker (struct buffer *buf, ptrdiff_t charpos,
ptrdiff_t bytepos);
extern void syms_of_marker (void);
#endif

View file

@ -38,6 +38,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "frame.h"
#include "intervals.h"
#include "lisp.h"
#include "marker.h"
#include "pdumper.h"
#include "window.h"
#include "sysstdio.h"
@ -2116,8 +2117,6 @@ dump_marker (struct dump_context *ctx, const struct Lisp_Marker *marker)
{
dump_field_lv_rawptr (ctx, out, marker, &marker->buffer,
Lisp_Vectorlike, WEIGHT_NORMAL);
dump_field_lv_rawptr (ctx, out, marker, &marker->next,
Lisp_Vectorlike, WEIGHT_STRONG);
DUMP_FIELD_COPY (out, marker, charpos);
DUMP_FIELD_COPY (out, marker, bytepos);
}
@ -2166,6 +2165,46 @@ dump_interval_node (struct dump_context *ctx, struct itree_node *node)
return offset;
}
/*****************/
/* FIXME: YUCK!! */
/*****************/
typedef unsigned int m_index_t;
struct Lisp_Markers
{
m_index_t size;
m_index_t gap_beg;
m_index_t gap_end;
struct Lisp_Marker *markers[FLEXIBLE_ARRAY_MEMBER];
};
static dump_off
dump_markers (struct dump_context *ctx, const struct Lisp_Markers *t)
{
ptrdiff_t bytesize = (sizeof (struct Lisp_Markers)
+ t->size * sizeof (struct Lisp_Marker *));
struct Lisp_Markers *out = malloc (bytesize);
dump_object_start (ctx, out, bytesize);
DUMP_FIELD_COPY (out, t, size);
DUMP_FIELD_COPY (out, t, gap_beg);
DUMP_FIELD_COPY (out, t, gap_end);
for (m_index_t i = 0; i < t->size; i++)
if (t->markers[i])
dump_field_fixup_later (ctx, out, t, &t->markers[i]);
dump_off offset = dump_object_finish (ctx, out, bytesize);
free (out);
for (m_index_t i = 0; i < t->size; i++)
{
struct Lisp_Marker *m = t->markers[i];
if (m)
dump_remember_fixup_ptr_raw
(ctx,
offset + dump_offsetof (struct Lisp_Markers, markers[i]),
dump_marker (ctx, m));
}
return offset;
}
static dump_off
dump_overlay (struct dump_context *ctx, const struct Lisp_Overlay *overlay)
{
@ -2861,8 +2900,7 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer)
DUMP_FIELD_COPY (out, buffer, own_text.overlay_unchanged_modified);
if (buffer->own_text.intervals)
dump_field_fixup_later (ctx, out, buffer, &buffer->own_text.intervals);
dump_field_lv_rawptr (ctx, out, buffer, &buffer->own_text.markers,
Lisp_Vectorlike, WEIGHT_NORMAL);
dump_field_fixup_later (ctx, out, buffer, &buffer->own_text.all_markers);
DUMP_FIELD_COPY (out, buffer, own_text.inhibit_shrinking);
DUMP_FIELD_COPY (out, buffer, own_text.redisplay);
}
@ -2921,11 +2959,18 @@ dump_buffer (struct dump_context *ctx, const struct buffer *in_buffer)
dump_field_lv (ctx, out, buffer, &buffer->undo_list_,
WEIGHT_STRONG);
dump_off offset = finish_dump_pvec (ctx, &out->header);
if (!buffer->base_buffer && buffer->own_text.intervals)
dump_remember_fixup_ptr_raw
(ctx,
offset + dump_offsetof (struct buffer, own_text.intervals),
dump_interval_tree (ctx, buffer->own_text.intervals, 0));
if (!buffer->base_buffer)
{
if (buffer->own_text.intervals)
dump_remember_fixup_ptr_raw
(ctx,
offset + dump_offsetof (struct buffer, own_text.intervals),
dump_interval_tree (ctx, buffer->own_text.intervals, 0));
dump_remember_fixup_ptr_raw
(ctx,
offset + dump_offsetof (struct buffer, own_text.all_markers),
dump_markers (ctx, buffer->own_text.all_markers));
}
return offset;
}

View file

@ -22,6 +22,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include "sysstdio.h"
#include "lisp.h"
#include "marker.h"
#include "character.h"
#include "coding.h"
#include "buffer.h"

View file

@ -31,6 +31,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <fcntl.h>
#include "lisp.h"
#include "marker.h"
/* Only MS-DOS does not define `subprocesses'. */
#ifdef subprocesses

View file

@ -25,6 +25,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <stdlib.h>
#include "lisp.h"
#include "marker.h"
#include "character.h"
#include "buffer.h"
#include "syntax.h"

View file

@ -33,6 +33,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
#include "textconv.h"
#include "marker.h"
#include "buffer.h"
#include "syntax.h"
#include "blockinput.h"

View file

@ -21,6 +21,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <config.h>
#include "lisp.h"
#include "marker.h"
#include "buffer.h"
#include "keyboard.h"
@ -128,9 +129,9 @@ record_marker_adjustments (ptrdiff_t from, ptrdiff_t to)
{
prepare_record ();
for (struct Lisp_Marker *m = BUF_MARKERS (current_buffer); m; m = m->next)
MARKERS_DO_ALL (it, BUF_ALL_MARKERS (current_buffer))
{
ptrdiff_t charpos = m->charpos;
ptrdiff_t charpos = it.m->charpos;
eassert (charpos <= Z);
if (from <= charpos && charpos <= to)
@ -142,11 +143,11 @@ record_marker_adjustments (ptrdiff_t from, ptrdiff_t to)
insertion_type t markers will automatically move forward
upon re-inserting the deleted text, so we have to arrange
for them to move backward to the correct position. */
ptrdiff_t adjustment = (m->insertion_type ? to : from) - charpos;
ptrdiff_t adjustment = (it.m->insertion_type ? to : from) - charpos;
if (adjustment)
{
Lisp_Object marker = make_lisp_ptr (m, Lisp_Vectorlike);
Lisp_Object marker = make_lisp_ptr (it.m, Lisp_Vectorlike);
bset_undo_list
(current_buffer,
Fcons (Fcons (marker, make_fixnum (adjustment)),

View file

@ -26,6 +26,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#endif
#include "lisp.h"
#include "marker.h"
#include "buffer.h"
#include "keyboard.h"
#include "keymap.h"

View file

@ -471,6 +471,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
#include <math.h>
#include "lisp.h"
#include "marker.h"
#include "atimer.h"
#include "composite.h"
#include "keyboard.h"