patches: fix org fold/unfold VoiceOver refresh; revert to BUF_MODIFF

REGRESSION: fold/unfold in org-mode, outline-mode and hideshow-mode did
not refresh VoiceOver text because ensureTextCache used BUF_CHARS_MODIFF
which is NOT bumped by (put-text-property ... 'invisible), the mechanism
used by modern org-fold-core (org >= 29) and outline-mode to hide text.

VoiceOver would continue reading folded content as if visible, or miss
newly unfolded content entirely, because the text cache was considered
valid despite the visible-text having changed.

Revert ensureTextCache to BUF_MODIFF with an explanatory comment:

- BUF_CHARS_MODIFF is bumped only on character insertions/deletions, not
  text-property changes.  Fold/unfold uses text properties for visibility.
- BUF_OVERLAY_MODIFF alone is also insufficient: org >= 29 uses text
  properties, not overlays, for folding.  Also hl-line-mode bumps
  BUF_OVERLAY_MODIFF every post-command-hook --- same per-keystroke cost
  as BUF_MODIFF, with none of its correctness guarantee.
- BUF_MODIFF cost is acceptable: ensureTextCache is called only when
  VoiceOver queries AX properties (human interaction speed, not redisplay
  speed).  Rebuild cost is O(visible-buffer-text).

Also retain C-n/C-p line-read fix from previous commit (7a0b4f6):
FocusedUIElementChanged excluded for sequential isCtrlNP moves.
This commit is contained in:
2026-03-02 20:57:32 +01:00
parent 7a0b4f6cf2
commit a5ff8d391b
9 changed files with 94 additions and 81 deletions

View File

@@ -1,4 +1,4 @@
From 3f97f3b69fdb10c1781ded98292434525838a369 Mon Sep 17 00:00:00 2001 From 2d2b7eb2b3039f3e581460c874a5ece52ebfdb9a Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 22:39:35 +0100 Date: Sat, 28 Feb 2026 22:39:35 +0100
Subject: [PATCH 0/8] ns: integrate with macOS Zoom for cursor tracking Subject: [PATCH 0/8] ns: integrate with macOS Zoom for cursor tracking

View File

@@ -1,4 +1,4 @@
From 234da3ced54798fe9e4bafb0eae08d571a4ffcfc Mon Sep 17 00:00:00 2001 From 01f32063667eb000b95b1514b0f78056aaa53c28 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 1/8] ns: add accessibility base classes and text extraction Subject: [PATCH 1/8] ns: add accessibility base classes and text extraction

View File

@@ -1,4 +1,4 @@
From 5a786a29a4d4067ce7a75994136f945c49e4624e Mon Sep 17 00:00:00 2001 From 4f4fa019e14e1cd9283b09b9b7cf20e772edc809 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 2/8] ns: implement buffer accessibility element (core Subject: [PATCH 2/8] ns: implement buffer accessibility element (core

View File

@@ -1,4 +1,4 @@
From 1dd6de1a46d86a87640129745bb0db01618b2879 Mon Sep 17 00:00:00 2001 From 4a77386e8210b2ad8fe1c9a0145cbde96d282c7c Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 3/8] ns: add buffer notification dispatch and mode-line Subject: [PATCH 3/8] ns: add buffer notification dispatch and mode-line

View File

@@ -1,4 +1,4 @@
From 3b8838647b39912753157d76b2aa4d8d0da0c55c Mon Sep 17 00:00:00 2001 From f6a4baf7e19aa2b95becff1dc8be4c2fdc85a0d5 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 4/8] ns: add interactive span elements for Tab navigation Subject: [PATCH 4/8] ns: add interactive span elements for Tab navigation

View File

@@ -1,4 +1,4 @@
From d4cda4bda0bee73c14946f20322975edd1580d46 Mon Sep 17 00:00:00 2001 From 825798ddb922cb24cf3db72d76c6ea4e29596844 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay

View File

@@ -1,4 +1,4 @@
From 33ed790921c1d78dec79f803807deae65fff365e Mon Sep 17 00:00:00 2001 From b0a0cf378168cd15d2af663100dae87ded394801 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 12:58:11 +0100 Date: Sat, 28 Feb 2026 12:58:11 +0100
Subject: [PATCH 6/8] doc: add VoiceOver accessibility section to macOS Subject: [PATCH 6/8] doc: add VoiceOver accessibility section to macOS

View File

@@ -1,4 +1,4 @@
From 8c99359156443223d13905de4cfbca58fb3e1177 Mon Sep 17 00:00:00 2001 From 1a3dea1876ecde2e625d3a2b8f41d688568ecbf8 Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Mon, 2 Mar 2026 18:39:46 +0100 Date: Mon, 2 Mar 2026 18:39:46 +0100
Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver
@@ -18,8 +18,8 @@ ValueChanged; keep overlay_modiff out of ensureTextCache to prevent a
race where an AX query consumes the change before notification. race where an AX query consumes the change before notification.
--- ---
src/nsterm.h | 1 + src/nsterm.h | 1 +
src/nsterm.m | 318 +++++++++++++++++++++++++++++++++++++++++++++------ src/nsterm.m | 348 ++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 283 insertions(+), 36 deletions(-) 2 files changed, 307 insertions(+), 42 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index 5746e9e9bd..21a93bc799 100644 index 5746e9e9bd..21a93bc799 100644
@@ -34,7 +34,7 @@ index 5746e9e9bd..21a93bc799 100644
@property (nonatomic, assign) BOOL cachedMarkActive; @property (nonatomic, assign) BOOL cachedMarkActive;
@property (nonatomic, copy) NSString *cachedCompletionAnnouncement; @property (nonatomic, copy) NSString *cachedCompletionAnnouncement;
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index a0598a73c2..3d8a5dd0fc 100644 index a0598a73c2..8f744d1bf3 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7263,11 +7263,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7263,11 +7263,154 @@ Accessibility virtual elements (macOS / Cocoa only)
@@ -228,7 +228,7 @@ index a0598a73c2..3d8a5dd0fc 100644
write section at the end needs synchronization to protect write section at the end needs synchronization to protect
against concurrent reads from AX server thread. */ against concurrent reads from AX server thread. */
eassert ([NSThread isMainThread]); eassert ([NSThread isMainThread]);
@@ -8005,24 +8149,15 @@ - (void)ensureTextCache @@ -8005,25 +8149,34 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -247,19 +247,62 @@ index a0598a73c2..3d8a5dd0fc 100644
- explicit announcements in postAccessibilityNotificationsForFrame). - explicit announcements in postAccessibilityNotificationsForFrame).
- Including overlay_modiff would silently update cachedOverlayModiff - Including overlay_modiff would silently update cachedOverlayModiff
- and prevent the notification dispatch from detecting changes. */ - and prevent the notification dispatch from detecting changes. */
ptrdiff_t chars_modiff = BUF_CHARS_MODIFF (b); - ptrdiff_t chars_modiff = BUF_CHARS_MODIFF (b);
+ /* Use BUF_MODIFF, not BUF_CHARS_MODIFF, for cache validity.
+
+ Fold/unfold commands (org-mode, outline-mode, hideshow-mode) change
+ text visibility by modifying the 'invisible text property via
+ `put-text-property' or `add-text-properties'. These bump BUF_MODIFF
+ but NOT BUF_CHARS_MODIFF, because no characters are inserted or
+ deleted. Using only BUF_CHARS_MODIFF would serve stale AX text
+ across fold/unfold: VoiceOver would continue reading hidden content
+ as if it were visible, or miss newly revealed content entirely.
+
+ BUF_MODIFF is bumped by all buffer modifications including
+ text-property changes (e.g. font-lock face assignments), causing a
+ full text-cache rebuild on each redisplay cycle. This is acceptable
+ because `ensureTextCache' is only called when VoiceOver queries
+ accessibilityValue or related AX properties --- which happens at
+ human interaction speed, not at redisplay speed. The per-rebuild
+ cost is O(visible-buffer-text).
+
+ Do NOT use BUF_OVERLAY_MODIFF alone: org-mode >= 29 (org-fold-core)
+ uses text properties, not overlays, for folding, so
+ BUF_OVERLAY_MODIFF would miss those changes. Additionally, modes
+ like hl-line-mode bump BUF_OVERLAY_MODIFF on every
+ post-command-hook, yielding the same per-keystroke rebuild cost as
+ BUF_MODIFF, with none of its correctness guarantee. */
+ ptrdiff_t modiff = BUF_MODIFF (b);
ptrdiff_t pt = BUF_PT (b); ptrdiff_t pt = BUF_PT (b);
NSUInteger textLen = cachedText ? [cachedText length] : 0; NSUInteger textLen = cachedText ? [cachedText length] : 0;
+ /* Cache validity: track BUF_MODIFF and buffer narrowing. - if (cachedText && cachedTextModiff == chars_modiff
+ Do NOT track BUF_OVERLAY_MODIFF here --- overlay text is not + if (cachedText && cachedTextModiff == modiff
+ included in the cached AX text (it is handled separately via
+ explicit announcements). Including overlay_modiff would
+ silently update cachedOverlayModiff and prevent the
+ notification dispatch from detecting overlay changes. */
if (cachedText && cachedTextModiff == chars_modiff
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
@@ -8108,7 +8243,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos && (textLen == 0
@@ -8039,7 +8192,7 @@ included in the cached AX text (it is handled separately via
{
[cachedText release];
cachedText = [text retain];
- cachedTextModiff = chars_modiff;
+ cachedTextModiff = modiff;
cachedTextStart = start;
if (visibleRuns)
@@ -8051,9 +8204,9 @@ included in the cached AX text (it is handled separately via
Walk the cached text once, recording the start offset of each
line. Uses NSString lineRangeForRange: --- O(N) in the total
text --- but this loop runs only on cache rebuild, which is
- gated on BUF_CHARS_MODIFF: actual character insertions or
- deletions. Font-lock (text property changes) does not trigger
- a rebuild, so the hot path (cursor movement, redisplay) never
+ gated on BUF_MODIFF changes. Rebuilds happen when any buffer
+ modification occurs (including fold/unfold), ensuring the line
+ index always matches the currently visible text.
enters this code. */
if (lineStartOffsets)
xfree (lineStartOffsets);
@@ -8108,7 +8261,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos
/* Binary search: runs are sorted by charpos (ascending). Find the /* Binary search: runs are sorted by charpos (ascending). Find the
run whose [charpos, charpos+length) range contains the target, run whose [charpos, charpos+length) range contains the target,
or the nearest run after an invisible gap. O(log n) instead of or the nearest run after an invisible gap. O(log n) instead of
@@ -268,7 +311,7 @@ index a0598a73c2..3d8a5dd0fc 100644
NSUInteger lo = 0, hi = visibleRunCount; NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi) while (lo < hi)
{ {
@@ -8157,10 +8292,10 @@ by run length (visible window), not total buffer size. */ @@ -8157,10 +8310,10 @@ by run length (visible window), not total buffer size. */
/* Convert accessibility string index to buffer charpos. /* Convert accessibility string index to buffer charpos.
Safe to call from any thread: uses only cachedText (NSString) and Safe to call from any thread: uses only cachedText (NSString) and
@@ -281,7 +324,7 @@ index a0598a73c2..3d8a5dd0fc 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -8202,7 +8337,7 @@ the slow path (composed character sequence walk), which is @@ -8202,7 +8355,7 @@ the slow path (composed character sequence walk), which is
return cp; return cp;
} }
} }
@@ -290,7 +333,7 @@ index a0598a73c2..3d8a5dd0fc 100644
if (lo > 0) if (lo > 0)
{ {
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -8224,7 +8359,7 @@ the slow path (composed character sequence walk), which is @@ -8224,7 +8377,7 @@ the slow path (composed character sequence walk), which is
deadlocking the AX server thread. This is prevented by: deadlocking the AX server thread. This is prevented by:
1. validWindow checks WINDOW_LIVE_P and BUFFERP before every 1. validWindow checks WINDOW_LIVE_P and BUFFERP before every
@@ -299,7 +342,7 @@ index a0598a73c2..3d8a5dd0fc 100644
2. All dispatch_sync blocks run on the main thread where no 2. All dispatch_sync blocks run on the main thread where no
concurrent Lisp code can modify state between checks. concurrent Lisp code can modify state between checks.
3. block_input prevents timer events and process output from 3. block_input prevents timer events and process output from
@@ -8570,6 +8705,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8570,6 +8723,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -350,7 +393,7 @@ index a0598a73c2..3d8a5dd0fc 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8792,7 +8971,7 @@ - (NSRect)accessibilityFrame @@ -8792,7 +8989,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -359,7 +402,7 @@ index a0598a73c2..3d8a5dd0fc 100644
These methods notify VoiceOver of text and selection changes. These methods notify VoiceOver of text and selection changes.
Called from the redisplay cycle (postAccessibilityUpdates). Called from the redisplay cycle (postAccessibilityUpdates).
@@ -8807,7 +8986,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8807,7 +9004,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -368,7 +411,7 @@ index a0598a73c2..3d8a5dd0fc 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8826,7 +9005,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8826,7 +9023,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
/* Update cachedPoint here so the selection-move branch does NOT /* Update cachedPoint here so the selection-move branch does NOT
fire for point changes caused by edits. WebKit and Chromium fire for point changes caused by edits. WebKit and Chromium
never send both ValueChanged and SelectedTextChanged for the never send both ValueChanged and SelectedTextChanged for the
@@ -377,7 +420,7 @@ index a0598a73c2..3d8a5dd0fc 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -9220,16 +9399,83 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9220,16 +9417,83 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
BOOL markActive = !NILP (BVAR (b, mark_active)); BOOL markActive = !NILP (BVAR (b, mark_active));
/* --- Text changed (edit) --- */ /* --- Text changed (edit) --- */
@@ -465,7 +508,7 @@ index a0598a73c2..3d8a5dd0fc 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -12402,7 +12648,7 @@ - (int) fullscreenState @@ -12402,7 +12666,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -474,7 +517,7 @@ index a0598a73c2..3d8a5dd0fc 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -12436,7 +12682,7 @@ - (int) fullscreenState @@ -12436,7 +12700,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -483,7 +526,7 @@ index a0598a73c2..3d8a5dd0fc 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12548,7 +12794,7 @@ - (void)postAccessibilityUpdates @@ -12548,7 +12812,7 @@ - (void)postAccessibilityUpdates
accessibilityUpdating = YES; accessibilityUpdating = YES;
/* Detect window tree change (split, delete, new buffer). Compare /* Detect window tree change (split, delete, new buffer). Compare
@@ -492,7 +535,7 @@ index a0598a73c2..3d8a5dd0fc 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12557,12 +12803,12 @@ - (void)postAccessibilityUpdates @@ -12557,12 +12821,12 @@ - (void)postAccessibilityUpdates
} }
/* If tree is stale, rebuild FIRST so we don't iterate freed /* If tree is stale, rebuild FIRST so we don't iterate freed

View File

@@ -1,4 +1,4 @@
From 33333f637c51c1ee2080c780fd623e67d3a85545 Mon Sep 17 00:00:00 2001 From f26d6d8d2a9030af3296902b4e3db79fef3ed760 Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz> From: Daneel <daneel@sukany.cz>
Date: Mon, 2 Mar 2026 18:49:13 +0100 Date: Mon, 2 Mar 2026 18:49:13 +0100
Subject: [PATCH 8/8] ns: announce child frame completion candidates for Subject: [PATCH 8/8] ns: announce child frame completion candidates for
@@ -33,8 +33,8 @@ area announcements.
doc/emacs/macos.texi | 14 +- doc/emacs/macos.texi | 14 +-
etc/NEWS | 18 +- etc/NEWS | 18 +-
src/nsterm.h | 20 ++ src/nsterm.h | 20 ++
src/nsterm.m | 456 +++++++++++++++++++++++++++++++++++++++---- src/nsterm.m | 436 ++++++++++++++++++++++++++++++++++++++++---
4 files changed, 460 insertions(+), 48 deletions(-) 4 files changed, 446 insertions(+), 42 deletions(-)
diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi
index 8d4a7825d8..03a657f970 100644 index 8d4a7825d8..03a657f970 100644
@@ -149,7 +149,7 @@ index 21a93bc799..bdd40b8eb7 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 3d8a5dd0fc..7555ae3e95 100644 index 8f744d1bf3..b434a7fd41 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1126,24 +1126,19 @@ Uses CFAbsoluteTimeGetCurrent() (~5 ns, a VDSO read) for timing. */ @@ -1126,24 +1126,19 @@ Uses CFAbsoluteTimeGetCurrent() (~5 ns, a VDSO read) for timing. */
@@ -300,37 +300,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
/* Build accessibility text for window W, skipping invisible text. /* Build accessibility text for window W, skipping invisible text.
Populates *OUT_START with the buffer start charpos. Populates *OUT_START with the buffer start charpos.
Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS
@@ -8149,15 +8250,23 @@ - (void)ensureTextCache @@ -8605,6 +8706,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range
if (!b)
return;
+ /* Use BUF_CHARS_MODIFF, not BUF_MODIFF, for cache validity.
+ BUF_MODIFF is bumped by every text-property change, including
+ font-lock face applications on every redisplay. AX text contains
+ only characters, not face data, so property-only changes do not
+ affect the cached value. Rebuilding the full buffer text on
+ each font-lock pass is O(buffer-size) per redisplay --- this
+ causes progressive slowdown when scrolling through large files.
+ BUF_CHARS_MODIFF is bumped only on actual character insertions
+ and deletions, matching the semantic of "did the text change".
+ This is the pattern used by WebKit and NSTextView.
+ Do NOT add BUF_OVERLAY_MODIFF here: modes like hl-line-mode move
+ overlays on every post-command-hook, bumping BUF_OVERLAY_MODIFF and
+ causing O(buffer-size) text rebuilds per keystroke. Fold/unfold
+ changes visible characters and thereby bumps BUF_CHARS_MODIFF. */
ptrdiff_t chars_modiff = BUF_CHARS_MODIFF (b);
ptrdiff_t pt = BUF_PT (b);
NSUInteger textLen = cachedText ? [cachedText length] : 0;
- /* Cache validity: track BUF_MODIFF and buffer narrowing.
- Do NOT track BUF_OVERLAY_MODIFF here --- overlay text is not
- included in the cached AX text (it is handled separately via
- explicit announcements). Including overlay_modiff would
- silently update cachedOverlayModiff and prevent the
- notification dispatch from detecting overlay changes. */
if (cachedText && cachedTextModiff == chars_modiff
&& cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart
@@ -8587,6 +8696,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range
[self ensureTextCache]; [self ensureTextCache];
@@ -342,7 +312,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
specpdl_ref count = SPECPDL_INDEX (); specpdl_ref count = SPECPDL_INDEX ();
record_unwind_current_buffer (); record_unwind_current_buffer ();
/* Ensure block_input is always matched by unblock_input even if /* Ensure block_input is always matched by unblock_input even if
@@ -9042,15 +9156,23 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point @@ -9060,15 +9166,23 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point
= @(ns_ax_text_state_change_selection_move); = @(ns_ax_text_state_change_selection_move);
moveInfo[@"AXTextSelectionDirection"] = @(direction); moveInfo[@"AXTextSelectionDirection"] = @(direction);
moveInfo[@"AXTextChangeElement"] = self; moveInfo[@"AXTextChangeElement"] = self;
@@ -372,7 +342,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
NSAccessibilitySelectedTextChangedNotification, NSAccessibilitySelectedTextChangedNotification,
moveInfo); moveInfo);
@@ -9148,12 +9270,17 @@ user expectation ("w" jumps to next word and reads it). */ @@ -9166,12 +9280,17 @@ user expectation ("w" jumps to next word and reads it). */
} }
} }
@@ -395,7 +365,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
if (cachedText if (cachedText
&& granularity == ns_ax_text_selection_granularity_line) && granularity == ns_ax_text_selection_granularity_line)
{ {
@@ -9218,6 +9345,11 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9236,6 +9355,11 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
block_input (); block_input ();
specpdl_ref count2 = SPECPDL_INDEX (); specpdl_ref count2 = SPECPDL_INDEX ();
@@ -407,7 +377,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
record_unwind_protect_void (unblock_input); record_unwind_protect_void (unblock_input);
record_unwind_current_buffer (); record_unwind_current_buffer ();
if (b != current_buffer) if (b != current_buffer)
@@ -9394,12 +9526,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9412,12 +9536,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
if (!b) if (!b)
return; return;
@@ -437,7 +407,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
if (modiff != self.cachedModiff) if (modiff != self.cachedModiff)
{ {
self.cachedModiff = modiff; self.cachedModiff = modiff;
@@ -9413,6 +9562,7 @@ Text property changes (e.g. face updates from @@ -9431,6 +9572,7 @@ Text property changes (e.g. face updates from
{ {
self.cachedCharsModiff = chars_modiff; self.cachedCharsModiff = chars_modiff;
[self postTextChangedNotification:point]; [self postTextChangedNotification:point];
@@ -445,7 +415,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
} }
} }
@@ -9435,8 +9585,15 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9453,8 +9595,15 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
displayed in the minibuffer. In normal editing buffers, displayed in the minibuffer. In normal editing buffers,
font-lock and other modes change BUF_OVERLAY_MODIFF on font-lock and other modes change BUF_OVERLAY_MODIFF on
every redisplay, triggering O(overlays) work per keystroke. every redisplay, triggering O(overlays) work per keystroke.
@@ -463,7 +433,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
goto skip_overlay_scan; goto skip_overlay_scan;
int selected_line = -1; int selected_line = -1;
@@ -9482,7 +9639,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9500,7 +9649,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
self.cachedPoint = point; self.cachedPoint = point;
self.cachedMarkActive = markActive; self.cachedMarkActive = markActive;
@@ -483,7 +453,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
NSInteger direction = ns_ax_text_selection_direction_discontiguous; NSInteger direction = ns_ax_text_selection_direction_discontiguous;
if (point > oldPoint) if (point > oldPoint)
direction = ns_ax_text_selection_direction_next; direction = ns_ax_text_selection_direction_next;
@@ -9716,6 +9884,13 @@ - (NSRect)accessibilityFrame @@ -9734,6 +9894,13 @@ - (NSRect)accessibilityFrame
if (vis_start >= vis_end) if (vis_start >= vis_end)
return @[]; return @[];
@@ -497,7 +467,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
block_input (); block_input ();
specpdl_ref blk_count = SPECPDL_INDEX (); specpdl_ref blk_count = SPECPDL_INDEX ();
record_unwind_protect_void (unblock_input); record_unwind_protect_void (unblock_input);
@@ -9840,6 +10015,7 @@ than O(chars). Fall back to pos+1 as safety net. */ @@ -9858,6 +10025,7 @@ than O(chars). Fall back to pos+1 as safety net. */
pos = span_end; pos = span_end;
} }
@@ -505,7 +475,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
return [[spans copy] autorelease]; return [[spans copy] autorelease];
} }
@@ -10021,6 +10197,10 @@ - (void)dealloc @@ -10039,6 +10207,10 @@ - (void)dealloc
#endif #endif
[accessibilityElements release]; [accessibilityElements release];
@@ -516,7 +486,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11470,6 +11650,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f @@ -11488,6 +11660,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO; windowClosing = NO;
processingCompose = NO; processingCompose = NO;
@@ -526,7 +496,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
scrollbarsNeedingUpdate = 0; scrollbarsNeedingUpdate = 0;
fs_state = FULLSCREEN_NONE; fs_state = FULLSCREEN_NONE;
fs_before_fs = next_maximized = -1; fs_before_fs = next_maximized = -1;
@@ -12778,6 +12961,154 @@ - (id)accessibilityFocusedUIElement @@ -12796,6 +12971,154 @@ - (id)accessibilityFocusedUIElement
The existing elements carry cached state (modiff, point) from the The existing elements carry cached state (modiff, point) from the
previous redisplay cycle. Rebuilding first would create fresh previous redisplay cycle. Rebuilding first would create fresh
elements with current values, making change detection impossible. */ elements with current values, making change detection impossible. */
@@ -681,7 +651,7 @@ index 3d8a5dd0fc..7555ae3e95 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12788,11 +13119,64 @@ - (void)postAccessibilityUpdates @@ -12806,11 +13129,64 @@ - (void)postAccessibilityUpdates
/* Re-entrance guard: VoiceOver callbacks during notification posting /* Re-entrance guard: VoiceOver callbacks during notification posting
can trigger redisplay, which calls ns_update_end, which calls us can trigger redisplay, which calls ns_update_end, which calls us