summaryrefslogtreecommitdiff
path: root/src/marker.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/marker.c')
-rw-r--r--src/marker.c132
1 files changed, 84 insertions, 48 deletions
diff --git a/src/marker.c b/src/marker.c
index 2f7e649e9a6..0ed1e55ddc9 100644
--- a/src/marker.c
+++ b/src/marker.c
@@ -1,5 +1,5 @@
/* Markers: examining, setting and deleting.
- Copyright (C) 1985, 1997-1998, 2001-2017 Free Software Foundation,
+ Copyright (C) 1985, 1997-1998, 2001-2022 Free Software Foundation,
Inc.
This file is part of GNU Emacs.
@@ -30,7 +30,7 @@ along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>. */
static ptrdiff_t cached_charpos;
static ptrdiff_t cached_bytepos;
static struct buffer *cached_buffer;
-static EMACS_INT cached_modiff;
+static modiff_count cached_modiff;
/* Juanma Barranquero <lekktu@gmail.com> reported ~3x increased
bootstrap time when byte_char_debug_check is enabled; so this
@@ -90,7 +90,7 @@ clear_charpos_cache (struct buffer *b)
#define CONSIDER(CHARPOS, BYTEPOS) \
{ \
ptrdiff_t this_charpos = (CHARPOS); \
- bool changed = 0; \
+ bool changed = false; \
\
if (this_charpos == charpos) \
{ \
@@ -105,14 +105,14 @@ clear_charpos_cache (struct buffer *b)
{ \
best_above = this_charpos; \
best_above_byte = (BYTEPOS); \
- changed = 1; \
+ changed = true; \
} \
} \
else if (this_charpos > best_below) \
{ \
best_below = this_charpos; \
best_below_byte = (BYTEPOS); \
- changed = 1; \
+ changed = true; \
} \
\
if (changed) \
@@ -133,6 +133,28 @@ CHECK_MARKER (Lisp_Object x)
CHECK_TYPE (MARKERP (x), Qmarkerp, x);
}
+/* 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).
+ But if there are many markers, it can take too much time to find a "good"
+ marker from which to start. Worse yet: if it takes a long time and we end
+ up finding a nearby markers, we won't add a new marker to cache this
+ result, so next time around we'll have to go through this same long list
+ to (re)find this best marker. So the further down the list of
+ markers we go, the less demanding we are w.r.t what is a good marker.
+
+ The previous code used INITIAL=50 and INCREMENT=0 and this lead to
+ really poor performance when there are many markers.
+ I haven't tried to tweak INITIAL, but experiments on my trusty Thinkpad
+ T61 using various artificial test cases seem to suggest that INCREMENT=50
+ might be "the best compromise": it significantly improved the
+ worst case and it was rarely slower and never by much.
+
+ 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
@@ -141,6 +163,7 @@ 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;
eassert (BUF_BEG (b) <= charpos && charpos <= BUF_Z (b));
@@ -180,22 +203,26 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
/* If we are down to a range of 50 chars,
don't bother checking any other markers;
scan the intervening chars directly now. */
- if (best_above - best_below < 50)
+ if (best_above - charpos < distance
+ || charpos - best_below < distance)
break;
+ else
+ distance += BYTECHAR_DISTANCE_INCREMENT;
}
/* 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. */
+ eassert (best_below <= charpos && charpos <= best_above);
if (charpos - best_below < best_above - charpos)
{
bool record = charpos - best_below > 5000;
- while (best_below != charpos)
+ while (best_below < charpos)
{
best_below++;
- BUF_INC_POS (b, best_below_byte);
+ best_below_byte += buf_next_char_len (b, best_below_byte);
}
/* If this position is quite far from the nearest known position,
@@ -217,10 +244,10 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
{
bool record = best_above - charpos > 5000;
- while (best_above != charpos)
+ while (best_above > charpos)
{
best_above--;
- BUF_DEC_POS (b, best_above_byte);
+ best_above_byte -= buf_prev_char_len (b, best_above_byte);
}
/* If this position is quite far from the nearest known position,
@@ -248,7 +275,7 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
#define CONSIDER(BYTEPOS, CHARPOS) \
{ \
ptrdiff_t this_bytepos = (BYTEPOS); \
- int changed = 0; \
+ int changed = false; \
\
if (this_bytepos == bytepos) \
{ \
@@ -263,14 +290,14 @@ buf_charpos_to_bytepos (struct buffer *b, ptrdiff_t charpos)
{ \
best_above = (CHARPOS); \
best_above_byte = this_bytepos; \
- changed = 1; \
+ changed = true; \
} \
} \
else if (this_bytepos > best_below_byte) \
{ \
best_below = (CHARPOS); \
best_below_byte = this_bytepos; \
- changed = 1; \
+ changed = true; \
} \
\
if (changed) \
@@ -293,6 +320,7 @@ 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;
eassert (BUF_BEG_BYTE (b) <= bytepos && bytepos <= BUF_Z_BYTE (b));
@@ -305,6 +333,10 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
if (best_above == best_above_byte)
return bytepos;
+ /* Check bytepos is not in the middle of a character. */
+ eassert (bytepos >= BUF_Z_BYTE (b)
+ || CHAR_HEAD_P (BUF_FETCH_BYTE (b, bytepos)));
+
best_below = BEG;
best_below_byte = BEG_BYTE;
@@ -323,8 +355,11 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
/* If we are down to a range of 50 chars,
don't bother checking any other markers;
scan the intervening chars directly now. */
- if (best_above - best_below < 50)
+ if (best_above - bytepos < distance
+ || bytepos - best_below < distance)
break;
+ else
+ distance += BYTECHAR_DISTANCE_INCREMENT;
}
/* We get here if we did not exactly hit one of the known places.
@@ -338,7 +373,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
while (best_below_byte < bytepos)
{
best_below++;
- BUF_INC_POS (b, best_below_byte);
+ best_below_byte += buf_next_char_len (b, best_below_byte);
}
/* If this position is quite far from the nearest known position,
@@ -365,7 +400,7 @@ buf_bytepos_to_charpos (struct buffer *b, ptrdiff_t bytepos)
while (best_above_byte > bytepos)
{
best_above--;
- BUF_DEC_POS (b, best_above_byte);
+ best_above_byte -= buf_prev_char_len (b, best_above_byte);
}
/* If this position is quite far from the nearest known position,
@@ -417,7 +452,7 @@ DEFUN ("marker-position", Fmarker_position, Smarker_position, 1, 1, 0,
{
CHECK_MARKER (marker);
if (XMARKER (marker)->buffer)
- return make_number (XMARKER (marker)->charpos);
+ return make_fixnum (XMARKER (marker)->charpos);
return Qnil;
}
@@ -491,11 +526,22 @@ set_marker_internal (Lisp_Object marker, Lisp_Object position,
{
register ptrdiff_t charpos, bytepos;
- /* Do not use CHECK_NUMBER_COERCE_MARKER because we
+ /* 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 (INTEGERP (position))
- charpos = XINT (position), bytepos = -1;
+ 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;
@@ -530,7 +576,7 @@ POSITION is nil, makes marker point nowhere so it no longer slows down
editing in any buffer. Returns MARKER. */)
(Lisp_Object marker, Lisp_Object position, Lisp_Object buffer)
{
- return set_marker_internal (marker, position, buffer, 0);
+ return set_marker_internal (marker, position, buffer, false);
}
/* Like the above, but won't let the position be outside the visible part. */
@@ -539,7 +585,7 @@ Lisp_Object
set_marker_restricted (Lisp_Object marker, Lisp_Object position,
Lisp_Object buffer)
{
- return set_marker_internal (marker, position, buffer, 1);
+ return set_marker_internal (marker, position, buffer, true);
}
/* Set the position of MARKER, specifying both the
@@ -586,10 +632,18 @@ set_marker_restricted_both (Lisp_Object marker, Lisp_Object buffer,
return marker;
}
-/* Remove MARKER from the chain of whatever buffer it is in,
- leaving it points to nowhere. This is called during garbage
- collection, so we must be careful to ignore and preserve
- mark bits, including those in chain fields of markers. */
+/* Detach a marker so that it no longer points anywhere and no longer
+ slows down editing. Do not free the marker, though, as a change
+ function could have inserted it into an undo list (Bug#30931). */
+
+void
+detach_marker (Lisp_Object marker)
+{
+ Fset_marker (marker, Qnil, Qnil);
+}
+
+/* Remove MARKER from the chain of whatever buffer it is in. Set its
+ buffer NULL. */
void
unchain_marker (register struct Lisp_Marker *marker)
@@ -673,7 +727,7 @@ see `marker-insertion-type'. */)
register Lisp_Object new;
if (!NILP (marker))
- CHECK_TYPE (INTEGERP (marker) || MARKERP (marker), Qinteger_or_marker_p, marker);
+ CHECK_TYPE (FIXNUMP (marker) || MARKERP (marker), Qinteger_or_marker_p, marker);
new = Fmake_marker ();
Fset_marker (new, marker,
@@ -705,23 +759,6 @@ If TYPE is nil, it means the marker stays behind when you insert text at it. */
return type;
}
-DEFUN ("buffer-has-markers-at", Fbuffer_has_markers_at, Sbuffer_has_markers_at,
- 1, 1, 0,
- doc: /* Return t if there are markers pointing at POSITION in the current buffer. */)
- (Lisp_Object position)
-{
- register struct Lisp_Marker *tail;
- register ptrdiff_t charpos;
-
- charpos = clip_to_bounds (BEG, XINT (position), Z);
-
- for (tail = BUF_MARKERS (current_buffer); tail; tail = tail->next)
- if (tail->charpos == charpos)
- return Qt;
-
- return Qnil;
-}
-
#ifdef MARKER_DEBUG
/* For debugging -- count the markers in buffer BUF. */
@@ -744,13 +781,13 @@ count_markers (struct buffer *buf)
ptrdiff_t
verify_bytepos (ptrdiff_t charpos)
{
- ptrdiff_t below = 1;
- ptrdiff_t below_byte = 1;
+ ptrdiff_t below = BEG;
+ ptrdiff_t below_byte = BEG_BYTE;
while (below != charpos)
{
below++;
- BUF_INC_POS (current_buffer, below_byte);
+ below_byte += buf_next_char_len (current_buffer, below_byte);
}
return below_byte;
@@ -767,5 +804,4 @@ syms_of_marker (void)
defsubr (&Scopy_marker);
defsubr (&Smarker_insertion_type);
defsubr (&Sset_marker_insertion_type);
- defsubr (&Sbuffer_has_markers_at);
}