#include <config.h>
+#define BUFFER_INLINE EXTERN_INLINE
+
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/param.h>
Setting the default value also goes through the alist of buffers
and stores into each buffer that does not say it has a local value. */
-DECL_ALIGN (struct buffer, buffer_defaults);
+struct buffer alignas (GCALIGNMENT) buffer_defaults;
/* A Lisp_Object pointer to the above, used for staticpro */
/* This structure holds the names of symbols whose values may be
buffer-local. It is indexed and accessed in the same way as the above. */
-DECL_ALIGN (struct buffer, buffer_local_symbols);
+struct buffer alignas (GCALIGNMENT) buffer_local_symbols;
/* A Lisp_Object pointer to the above, used for staticpro */
static Lisp_Object Vbuffer_local_symbols;
/* Maximum length of an overlay vector. */
#define OVERLAY_COUNT_MAX \
((ptrdiff_t) min (MOST_POSITIVE_FIXNUM, \
- min (PTRDIFF_MAX, SIZE_MAX) / sizeof (Lisp_Object)))
+ min (PTRDIFF_MAX, SIZE_MAX) / word_size))
/* Flags indicating which built-in buffer-local variables
are permanent locals. */
static void free_buffer_text (struct buffer *b);
static struct Lisp_Overlay * copy_overlays (struct buffer *, struct Lisp_Overlay *);
static void modify_overlay (struct buffer *, ptrdiff_t, ptrdiff_t);
-static Lisp_Object buffer_lisp_local_variables (struct buffer *);
+static Lisp_Object buffer_lisp_local_variables (struct buffer *, int);
/* For debugging; temporary. See set_buffer_internal. */
/* Lisp_Object Qlisp_mode, Vcheck_symbol; */
/* An ordinary buffer uses its own struct buffer_text. */
b->text = &b->own_text;
- b->base_buffer = 0;
+ b->base_buffer = NULL;
+ /* No one shares the text with us now. */
+ b->indirections = 0;
BUF_GAP_SIZE (b) = 20;
BLOCK_INPUT;
BUF_CHARS_MODIFF (b) = 1;
BUF_OVERLAY_MODIFF (b) = 1;
BUF_SAVE_MODIFF (b) = 1;
- BUF_INTERVALS (b) = 0;
+ buffer_set_intervals (b, NULL);
BUF_UNCHANGED_MODIFIED (b) = 1;
BUF_OVERLAY_UNCHANGED_MODIFIED (b) = 1;
BUF_END_UNCHANGED (b) = 0;
BVAR (b, zv_marker) = Qnil;
name = Fcopy_sequence (buffer_or_name);
- STRING_SET_INTERVALS (name, NULL_INTERVAL);
+ string_set_intervals (name, NULL);
BVAR (b, name) = name;
BVAR (b, undo_list) = (SREF (name, 0) != ' ') ? Qnil : Qt;
static struct Lisp_Overlay *
copy_overlays (struct buffer *b, struct Lisp_Overlay *list)
{
- Lisp_Object buffer;
struct Lisp_Overlay *result = NULL, *tail = NULL;
- XSETBUFFER (buffer, b);
-
for (; list; list = list->next)
{
- Lisp_Object overlay, start, end, old_overlay;
- ptrdiff_t charpos;
-
- XSETMISC (old_overlay, list);
- charpos = marker_position (OVERLAY_START (old_overlay));
- start = Fmake_marker ();
- Fset_marker (start, make_number (charpos), buffer);
- XMARKER (start)->insertion_type
- = XMARKER (OVERLAY_START (old_overlay))->insertion_type;
-
- charpos = marker_position (OVERLAY_END (old_overlay));
- end = Fmake_marker ();
- Fset_marker (end, make_number (charpos), buffer);
- XMARKER (end)->insertion_type
- = XMARKER (OVERLAY_END (old_overlay))->insertion_type;
-
- overlay = allocate_misc ();
- XMISCTYPE (overlay) = Lisp_Misc_Overlay;
- OVERLAY_START (overlay) = start;
- OVERLAY_END (overlay) = end;
- OVERLAY_PLIST (overlay) = Fcopy_sequence (OVERLAY_PLIST (old_overlay));
- XOVERLAY (overlay)->next = NULL;
+ Lisp_Object overlay, start, end;
+ struct Lisp_Marker *m;
+ eassert (MARKERP (list->start));
+ m = XMARKER (list->start);
+ start = build_marker (b, m->charpos, m->bytepos);
+ XMARKER (start)->insertion_type = m->insertion_type;
+
+ eassert (MARKERP (list->end));
+ m = XMARKER (list->end);
+ end = build_marker (b, m->charpos, m->bytepos);
+ XMARKER (end)->insertion_type = m->insertion_type;
+
+ overlay = build_overlay (start, end, Fcopy_sequence (list->plist));
if (tail)
tail = tail->next = XOVERLAY (overlay);
else
{
int offset;
- for_each_per_buffer_object_at (offset)
+ FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
{
Lisp_Object obj;
/* Get (a copy of) the alist of Lisp-level local variables of FROM
and install that in TO. */
- BVAR (to, local_var_alist) = buffer_lisp_local_variables (from);
+ BVAR (to, local_var_alist) = buffer_lisp_local_variables (from, 1);
}
b = allocate_buffer ();
+ /* No double indirection - if base buffer is indirect,
+ new buffer becomes an indirect to base's base. */
b->base_buffer = (XBUFFER (base_buffer)->base_buffer
? XBUFFER (base_buffer)->base_buffer
: XBUFFER (base_buffer));
/* Use the base buffer's text object. */
b->text = b->base_buffer->text;
+ /* We have no own text. */
+ b->indirections = -1;
+ /* Notify base buffer that we share the text now. */
+ b->base_buffer->indirections++;
b->pt = b->base_buffer->pt;
b->begv = b->base_buffer->begv;
all_buffers = b;
name = Fcopy_sequence (name);
- STRING_SET_INTERVALS (name, NULL_INTERVAL);
+ string_set_intervals (name, NULL);
BVAR (b, name) = name;
reset_buffer (b);
eassert (NILP (BVAR (b->base_buffer, begv_marker)));
eassert (NILP (BVAR (b->base_buffer, zv_marker)));
- BVAR (b->base_buffer, pt_marker)
+ BVAR (b->base_buffer, pt_marker)
= build_marker (b->base_buffer, b->base_buffer->pt, b->base_buffer->pt_byte);
BVAR (b->base_buffer, begv_marker)
drop_overlay (struct buffer *b, struct Lisp_Overlay *ov)
{
eassert (b == XBUFFER (Fmarker_buffer (ov->start)));
- modify_overlay (b, marker_position (ov->start), marker_position (ov->end));
+ modify_overlay (b, marker_position (ov->start),
+ marker_position (ov->end));
Fset_marker (ov->start, Qnil, Qnil);
Fset_marker (ov->end, Qnil, Qnil);
SET_PER_BUFFER_VALUE_P (b, i, 0);
/* For each slot that has a default value, copy that into the slot. */
- for_each_per_buffer_object_at (offset)
+ FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
{
int idx = PER_BUFFER_IDX (offset);
if ((idx > 0
{
/* Note fileio.c:make_temp_name does random differently. */
tem2 = concat2 (name, make_formatted_string
- (number, "-%"pI"d",
+ (number, "-%"pI"d",
XFASTINT (Frandom (make_number (999999)))));
tem = Fget_buffer (tem2);
if (NILP (tem))
/* Return an alist of the Lisp-level buffer-local bindings of
buffer BUF. That is, don't include the variables maintained
- in special slots in the buffer object. */
+ in special slots in the buffer object.
+ If CLONE is zero elements of the form (VAR . unbound) are replaced
+ by VAR. */
static Lisp_Object
-buffer_lisp_local_variables (struct buffer *buf)
+buffer_lisp_local_variables (struct buffer *buf, int clone)
{
Lisp_Object result = Qnil;
register Lisp_Object tail;
if (buf != current_buffer)
val = XCDR (elt);
- result = Fcons (EQ (val, Qunbound)
+ result = Fcons (!clone && EQ (val, Qunbound)
? XCAR (elt)
: Fcons (XCAR (elt), val),
result);
buf = XBUFFER (buffer);
}
- result = buffer_lisp_local_variables (buf);
+ result = buffer_lisp_local_variables (buf, 0);
/* Add on all the variables stored in special slots. */
{
int offset, idx;
- for_each_per_buffer_object_at (offset)
+ FOR_EACH_PER_BUFFER_OBJECT_AT (offset)
{
idx = PER_BUFFER_IDX (offset);
if ((idx == -1 || PER_BUFFER_VALUE_P (buf, idx))
return Qnil;
}
-/*
- DEFVAR_LISP ("kill-buffer-hook", ..., "\
-Hook to be run (by `run-hooks', which see) when a buffer is killed.\n\
-The buffer being killed will be current while the hook is running.\n\
+/* Truncate undo list and shrink the gap of BUFFER. */
+
+int
+compact_buffer (struct buffer *buffer)
+{
+ /* Verify indirection counters. */
+ if (buffer->base_buffer)
+ {
+ eassert (buffer->indirections == -1);
+ eassert (buffer->base_buffer->indirections > 0);
+ }
+ else
+ eassert (buffer->indirections >= 0);
+
+ /* Skip dead buffers, indirect buffers and buffers
+ which aren't changed since last compaction. */
+ if (!NILP (buffer->INTERNAL_FIELD (name))
+ && (buffer->base_buffer == NULL)
+ && (buffer->text->compact != buffer->text->modiff))
+ {
+ /* If a buffer's undo list is Qt, that means that undo is
+ turned off in that buffer. Calling truncate_undo_list on
+ Qt tends to return NULL, which effectively turns undo back on.
+ So don't call truncate_undo_list if undo_list is Qt. */
+ if (!EQ (buffer->INTERNAL_FIELD (undo_list), Qt))
+ truncate_undo_list (buffer);
+
+ /* Shrink buffer gaps. */
+ if (!buffer->text->inhibit_shrinking)
+ {
+ /* If a buffer's gap size is more than 10% of the buffer
+ size, or larger than 2000 bytes, then shrink it
+ accordingly. Keep a minimum size of 20 bytes. */
+ int size = min (2000, max (20, (buffer->text->z_byte / 10)));
+
+ if (buffer->text->gap_size > size)
+ {
+ struct buffer *save_current = current_buffer;
+ current_buffer = buffer;
+ make_gap (-(buffer->text->gap_size - size));
+ current_buffer = save_current;
+ }
+ }
+ buffer->text->compact = buffer->text->modiff;
+ return 1;
+ }
+ return 0;
+}
-Functions run by this hook are supposed to not change the current
-buffer. See `kill-buffer'."
-*/
DEFUN ("kill-buffer", Fkill_buffer, Skill_buffer, 0, 1, "bKill buffer: ",
doc: /* Kill the buffer specified by BUFFER-OR-NAME.
The argument may be a buffer or the name of an existing buffer.
if (EQ (buffer, XWINDOW (minibuf_window)->buffer))
return Qnil;
- /* When we kill a base buffer, kill all its indirect buffers.
+ /* When we kill an ordinary buffer which shares it's buffer text
+ with indirect buffer(s), we must kill indirect buffer(s) too.
We do it at this stage so nothing terrible happens if they
ask questions or their hooks get errors. */
- if (! b->base_buffer)
+ if (!b->base_buffer && b->indirections > 0)
{
struct buffer *other;
GCPRO1 (buffer);
- for (other = all_buffers; other; other = other->header.next.buffer)
- /* all_buffers contains dead buffers too;
- don't re-kill them. */
- if (other->base_buffer == b && !NILP (BVAR (other, name)))
+ FOR_EACH_BUFFER (other)
+ if (other->base_buffer == b)
{
Lisp_Object buf;
XSETBUFFER (buf, other);
m = next;
}
BUF_MARKERS (b) = NULL;
- BUF_INTERVALS (b) = NULL_INTERVAL;
+ buffer_set_intervals (b, NULL);
/* Perhaps we should explicitly free the interval tree here... */
}
BVAR (b, name) = Qnil;
BLOCK_INPUT;
- if (! b->base_buffer)
+ if (b->base_buffer)
+ {
+ /* Notify our base buffer that we don't share the text anymore. */
+ eassert (b->indirections == -1);
+ b->base_buffer->indirections--;
+ eassert (b->base_buffer->indirections >= 0);
+ }
+ else
+ /* No one shares our buffer text, can free it. */
free_buffer_text (b);
if (b->newline_cache)
Vinhibit_quit = tem;
/* Update buffer list of selected frame. */
- f->buffer_list = Fcons (buffer, Fdelq (buffer, f->buffer_list));
- f->buried_buffer_list = Fdelq (buffer, f->buried_buffer_list);
+ FSET (f, buffer_list, Fcons (buffer, Fdelq (buffer, f->buffer_list)));
+ FSET (f, buried_buffer_list, Fdelq (buffer, f->buried_buffer_list));
/* Run buffer-list-update-hook. */
if (!NILP (Vrun_hooks))
Vinhibit_quit = tem;
/* Update buffer lists of selected frame. */
- f->buffer_list = Fdelq (buffer, f->buffer_list);
- f->buried_buffer_list = Fcons (buffer, Fdelq (buffer, f->buried_buffer_list));
+ FSET (f, buffer_list, Fdelq (buffer, f->buffer_list));
+ FSET (f, buried_buffer_list,
+ Fcons (buffer, Fdelq (buffer, f->buried_buffer_list)));
/* Run buffer-list-update-hook. */
if (!NILP (Vrun_hooks))
{ /* This is probably harder to make work. */
struct buffer *other;
- for (other = all_buffers; other; other = other->header.next.buffer)
+ FOR_EACH_BUFFER (other)
if (other->base_buffer == other_buffer
|| other->base_buffer == current_buffer)
error ("One of the buffers to swap has indirect buffers");
swapfield (zv_byte, ptrdiff_t);
eassert (!current_buffer->base_buffer);
eassert (!other_buffer->base_buffer);
+ swapfield (indirections, ptrdiff_t);
current_buffer->clip_changed = 1; other_buffer->clip_changed = 1;
swapfield (newline_cache, struct region_cache *);
swapfield (width_run_cache, struct region_cache *);
&& (EQ (XWINDOW (w)->buffer, buf1)
|| EQ (XWINDOW (w)->buffer, buf2)))
Fset_marker (XWINDOW (w)->pointm,
- make_number (BUF_BEGV (XBUFFER (XWINDOW (w)->buffer))),
+ make_number
+ (BUF_BEGV (XBUFFER (XWINDOW (w)->buffer))),
XWINDOW (w)->buffer);
w = Fnext_window (w, Qt, Qt);
}
/* Copy this buffer's new multibyte status
into all of its indirect buffers. */
- for (other = all_buffers; other; other = other->header.next.buffer)
+ FOR_EACH_BUFFER (other)
if (other->base_buffer == current_buffer && !NILP (BVAR (other, name)))
{
BVAR (other, enable_multibyte_characters)
ptrdiff_t endpos;
XSETMISC (overlay ,tail);
- if (!OVERLAYP (overlay))
- abort ();
+ eassert (OVERLAYP (overlay));
endpos = OVERLAY_POSITION (OVERLAY_END (overlay));
if (endpos < pos)
ptrdiff_t startpos;
XSETMISC (overlay, tail);
- if (!OVERLAYP (overlay))
- abort ();
+ eassert (OVERLAYP (overlay));
startpos = OVERLAY_POSITION (OVERLAY_START (overlay));
if (pos < startpos)
Lisp_Object overlay;
overlay = overlay_vec[i];
- if (OVERLAY_VALID (overlay)
+ if (OVERLAYP (overlay)
&& OVERLAY_POSITION (OVERLAY_START (overlay)) > 0
&& OVERLAY_POSITION (OVERLAY_END (overlay)) > 0)
{
{
next = tail->next;
XSETMISC (overlay, tail);
-
- /* If the overlay is not valid, get rid of it. */
- if (!OVERLAY_VALID (overlay))
-#if 1
- abort ();
-#else
- {
- /* Splice the cons cell TAIL out of overlays_before. */
- if (!NILP (prev))
- XCDR (prev) = next;
- else
- buf->overlays_before = next;
- tail = prev;
- continue;
- }
-#endif
+ eassert (OVERLAYP (overlay));
beg = OVERLAY_START (overlay);
end = OVERLAY_END (overlay);
Lisp_Object otherbeg, otheroverlay;
XSETMISC (otheroverlay, other);
- eassert (OVERLAY_VALID (otheroverlay));
+ eassert (OVERLAYP (otheroverlay));
otherbeg = OVERLAY_START (otheroverlay);
if (OVERLAY_POSITION (otherbeg) >= where)
{
next = tail->next;
XSETMISC (overlay, tail);
-
- /* If the overlay is not valid, get rid of it. */
- if (!OVERLAY_VALID (overlay))
-#if 1
- abort ();
-#else
- {
- /* Splice the cons cell TAIL out of overlays_after. */
- if (!NILP (prev))
- XCDR (prev) = next;
- else
- buf->overlays_after = next;
- tail = prev;
- continue;
- }
-#endif
+ eassert (OVERLAYP (overlay));
beg = OVERLAY_START (overlay);
end = OVERLAY_END (overlay);
Lisp_Object otherend, otheroverlay;
XSETMISC (otheroverlay, other);
- eassert (OVERLAY_VALID (otheroverlay));
+ eassert (OVERLAYP (otheroverlay));
otherend = OVERLAY_END (otheroverlay);
if (OVERLAY_POSITION (otherend) <= where)
if (!NILP (rear_advance))
XMARKER (end)->insertion_type = 1;
- overlay = allocate_misc ();
- XMISCTYPE (overlay) = Lisp_Misc_Overlay;
- XOVERLAY (overlay)->start = beg;
- XOVERLAY (overlay)->end = end;
- XOVERLAY (overlay)->plist = Qnil;
- XOVERLAY (overlay)->next = NULL;
+ overlay = build_overlay (beg, end, Qnil);
/* Put the new overlay on the wrong list. */
end = OVERLAY_END (overlay);
}
/* It wasn't in the list, so add it to the front. */
changed = !NILP (value);
- XOVERLAY (overlay)->plist
- = Fcons (prop, Fcons (value, XOVERLAY (overlay)->plist));
+ set_overlay_plist
+ (overlay, Fcons (prop, Fcons (value, XOVERLAY (overlay)->plist)));
found:
if (! NILP (buffer))
{
ptrdiff_t i;
memcpy (copy, XVECTOR (last_overlay_modification_hooks)->contents,
- size * sizeof (Lisp_Object));
+ size * word_size);
gcpro1.var = copy;
gcpro1.nvars = size;
sure that this is still correct. Otherwise, mark_vectorlike may not
trace all Lisp_Objects in buffer_defaults and buffer_local_symbols. */
const int pvecsize
- = (offsetof (struct buffer, own_text) - sizeof (struct vectorlike_header))
- / sizeof (Lisp_Object);
+ = (offsetof (struct buffer, own_text) - header_size) / word_size;
memset (buffer_permanent_local_flags, 0, sizeof buffer_permanent_local_flags);
/* Prevent GC from getting confused. */
buffer_defaults.text = &buffer_defaults.own_text;
buffer_local_symbols.text = &buffer_local_symbols.own_text;
- BUF_INTERVALS (&buffer_defaults) = 0;
- BUF_INTERVALS (&buffer_local_symbols) = 0;
+ /* No one will share the text with these buffers, but let's play it safe. */
+ buffer_defaults.indirections = 0;
+ buffer_local_symbols.indirections = 0;
+ buffer_set_intervals (&buffer_defaults, NULL);
+ buffer_set_intervals (&buffer_local_symbols, NULL);
XSETPVECTYPESIZE (&buffer_defaults, PVEC_BUFFER, pvecsize);
XSETBUFFER (Vbuffer_defaults, &buffer_defaults);
XSETPVECTYPESIZE (&buffer_local_symbols, PVEC_BUFFER, pvecsize);
The local flag bits are in the local_var_flags slot of the buffer. */
/* Nothing can work if this isn't true */
- { verify (sizeof (EMACS_INT) == sizeof (Lisp_Object)); }
+ { verify (sizeof (EMACS_INT) == word_size); }
/* 0 means not a lisp var, -1 means always local, else mask */
memset (&buffer_local_flags, 0, sizeof buffer_local_flags);
Map new memory. */
struct buffer *b;
- for (b = all_buffers; b; b = b->header.next.buffer)
+ FOR_EACH_BUFFER (b)
if (b->text->beg == NULL)
enlarge_buffer_text (b, 0);
}
DEFSYM (Qkill_buffer_query_functions, "kill-buffer-query-functions");
Fput (Qprotected_field, Qerror_conditions,
- pure_cons (Qprotected_field, pure_cons (Qerror, Qnil)));
+ listn (CONSTYPE_PURE, 2, Qprotected_field, Qerror));
Fput (Qprotected_field, Qerror_message,
build_pure_c_string ("Attempt to modify a protected field"));
doc: /* Non-nil if searches and matches should ignore case. */);
DEFVAR_PER_BUFFER ("fill-column", &BVAR (current_buffer, fill_column),
- make_number (LISP_INT_TAG),
+ make_number (Lisp_Int0),
doc: /* Column beyond which automatic line-wrapping should happen.
Interactively, you can set the buffer local value using \\[set-fill-column]. */);
DEFVAR_PER_BUFFER ("left-margin", &BVAR (current_buffer, left_margin),
- make_number (LISP_INT_TAG),
+ make_number (Lisp_Int0),
doc: /* Column for the default `indent-line-function' to indent to.
Linefeed indents to this column in Fundamental mode. */);
DEFVAR_PER_BUFFER ("tab-width", &BVAR (current_buffer, tab_width),
- make_number (LISP_INT_TAG),
+ make_number (Lisp_Int0),
doc: /* Distance between tab stops (for display of tab characters), in columns.
This should be an integer greater than zero. */);
Backing up is done before the first time the file is saved. */);
DEFVAR_PER_BUFFER ("buffer-saved-size", &BVAR (current_buffer, save_length),
- make_number (LISP_INT_TAG),
+ make_number (Lisp_Int0),
doc: /* Length of current buffer when last read in, saved or auto-saved.
0 initially.
-1 means auto-saving turned off until next real save.
defsubr (&Smake_indirect_buffer);
defsubr (&Sgenerate_new_buffer_name);
defsubr (&Sbuffer_name);
-/*defsubr (&Sbuffer_number);*/
defsubr (&Sbuffer_file_name);
defsubr (&Sbuffer_base_buffer);
defsubr (&Sbuffer_local_value);