summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorPo Lu <luangruo@yahoo.com>2024-04-30 18:26:39 +0800
committerPo Lu <luangruo@yahoo.com>2024-04-30 18:28:05 +0800
commitb36fd07560fd12c5e819e808a6f0eb9579f77c25 (patch)
tree81de96a5c2b3c0f5e0465a028c8c6b98e4e65ce0 /src
parentaad80e1934f09b643b93aeb3bf9c1d583af6e2ec (diff)
Fix deletion of text holding `inhibit-read-only' properties
* src/intervals.h (INTERVAL_VISIBLE_P): Split into ... (INTERVAL_GENERALLY_WRITABLE_P, INTERVAL_EXPRESSLY_WRITABLE_P): ... two new macros. * src/textprop.c (verify_interval_modification): If the buffer is read only, verify not that there is only a single exempting interval spanning the area of a multiple-character operation, but that every intervening interval in such an operation exempts it from write restrictions, either by providing a read-only property that appears in Vinhibit_read_only, or by providing an inhibit-read-only property. * test/src/textprop-tests.el (textprop-interval-immutability): New test.
Diffstat (limited to 'src')
-rw-r--r--src/intervals.h23
-rw-r--r--src/textprop.c33
2 files changed, 41 insertions, 15 deletions
diff --git a/src/intervals.h b/src/intervals.h
index 610c803cc77..5c6ef33a3a9 100644
--- a/src/intervals.h
+++ b/src/intervals.h
@@ -204,14 +204,21 @@ set_interval_plist (INTERVAL i, Lisp_Object plist)
#define INTERVAL_VISIBLE_P(i) \
(i && NILP (textget ((i)->plist, Qinvisible)))
-/* Is this interval writable? Replace later with cache access. */
-#define INTERVAL_WRITABLE_P(i) \
- (NILP (textget ((i)->plist, Qread_only)) \
- || !NILP (textget ((i)->plist, Qinhibit_read_only)) \
- || ((CONSP (Vinhibit_read_only) \
- ? !NILP (Fmemq (textget ((i)->plist, Qread_only), \
- Vinhibit_read_only)) \
- : !NILP (Vinhibit_read_only))))
+/* Is this interval writable by virtue of not being marked read-only, or
+ a general value of Vinhibit_read_only? Replace later with cache
+ access. */
+#define INTERVAL_GENERALLY_WRITABLE_P(i, ro) \
+ (NILP (ro) || (!NILP (Vinhibit_read_only) \
+ && !CONSP (Vinhibit_read_only)))
+
+/* Is this interval writable by virtue of an explicit inhibit-read-only
+ property, or the specific presence of its Qread_only property in
+ Vinhibit_read_only? */
+#define INTERVAL_EXPRESSLY_WRITABLE_P(i, ro) \
+ (!NILP (textget ((i)->plist, Qinhibit_read_only)) \
+ || (!NILP (ro) \
+ && CONSP (Vinhibit_read_only) \
+ && !NILP (Fmemq ((ro), Vinhibit_read_only))))
/* Macros to tell whether insertions before or after this interval
should stick to it. Now we have Vtext_property_default_nonsticky,
diff --git a/src/textprop.c b/src/textprop.c
index 7d9aae0d2c5..84d6b5f1545 100644
--- a/src/textprop.c
+++ b/src/textprop.c
@@ -2186,6 +2186,7 @@ verify_interval_modification (struct buffer *buf,
{
INTERVAL intervals = buffer_intervals (buf);
INTERVAL i;
+ ptrdiff_t p;
Lisp_Object hooks;
Lisp_Object prev_mod_hooks;
Lisp_Object mod_hooks;
@@ -2314,14 +2315,30 @@ verify_interval_modification (struct buffer *buf,
}
else
{
+ bool buffer_read_only;
+
/* Loop over intervals on or next to START...END,
collecting their hooks. */
+ /* Extent of last writable interval. */
i = find_interval (intervals, start);
+ p = 0;
+ buffer_read_only = (!NILP (BVAR (current_buffer, read_only))
+ && NILP (Vinhibit_read_only));
do
{
- if (! INTERVAL_WRITABLE_P (i))
- text_read_only (textget (i->plist, Qread_only));
+ bool implied, express;
+ Lisp_Object read_only;
+
+ read_only = textget ((i)->plist, Qread_only);
+ implied = INTERVAL_GENERALLY_WRITABLE_P (i, read_only);
+ express = INTERVAL_EXPRESSLY_WRITABLE_P (i, read_only);
+ if (!implied && !express)
+ text_read_only (read_only);
+ /* If this interval is only implicitly read only and the
+ buffer is read only as a whole, signal an error. */
+ else if (!express && buffer_read_only)
+ xsignal1 (Qbuffer_read_only, Fcurrent_buffer ());
if (!inhibit_modification_hooks)
{
@@ -2333,16 +2350,18 @@ verify_interval_modification (struct buffer *buf,
}
}
- if (i->position + LENGTH (i) < end
- && (!NILP (BVAR (current_buffer, read_only))
- && NILP (Vinhibit_read_only)))
- xsignal1 (Qbuffer_read_only, Fcurrent_buffer ());
-
+ p = i->position + LENGTH (i);
i = next_interval (i);
}
/* Keep going thru the interval containing the char before END. */
while (i && i->position < end);
+ /* Should the buffer be read only while the last interval with an
+ `inhibit-read-only' property does not enclose the entire change
+ under consideration, signal error. */
+ if (p < end && buffer_read_only)
+ xsignal1 (Qbuffer_read_only, Fcurrent_buffer ());
+
if (!inhibit_modification_hooks)
{
hooks = Fnreverse (hooks);