patches: fix Blocker #2 - remove ensureTextCache from notification path

BUF_MODIFF was needed for fold/unfold correctness (org-mode), but
ensureTextCache was called from postAccessibilityNotificationsForFrame:
on every cursor move, causing O(buffer-size) rebuild on every font-lock
pass.

Fix: remove [self ensureTextCache] from the granularity-detection branch
of the notification path.  The granularity detection now uses cachedText
directly, falling back to granularity_unknown when absent (safe: VoiceOver
makes its own determination).  ensureTextCache is called exclusively from
AX getters (human interaction speed).  Font-lock passes no longer trigger
any cache rebuild.

The BUF_MODIFF validity in ensureTextCache is retained (correct for
fold/unfold). Comment updated to accurately describe the calling pattern.

All 9 patches apply cleanly on fresh base (git apply verified).
This commit is contained in:
2026-03-03 18:26:17 +01:00
parent 70f0cb9a86
commit f42e799991
2 changed files with 79 additions and 57 deletions

View File

@@ -1,4 +1,4 @@
From 7474a4e1ddbf37286842e3beda1810c40f2a3ef7 Mon Sep 17 00:00:00 2001 From 217177caefc709c37ae04732ec595ace903e4cc4 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Mon, 2 Mar 2026 18:39:46 +0100 Date: Mon, 2 Mar 2026 18:39:46 +0100
Subject: [PATCH 8/9] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH 8/9] ns: announce overlay completion candidates for VoiceOver
@@ -11,15 +11,24 @@ this change VoiceOver cannot read overlay-based completion UIs.
'current', 'selected', 'selection' in face symbol names. 'current', 'selected', 'selection' in face symbol names.
(ns_ax_selected_overlay_text): New function; scan overlay strings in (ns_ax_selected_overlay_text): New function; scan overlay strings in
the window for a line with a selected face; return its text. the window for a line with a selected face; return its text.
(EmacsAccessibilityBuffer(Notifications) (ensureTextCache): Switch cache-validity counter from BUF_CHARS_MODIFF
postAccessibilityNotificationsForFrame:): Handle BUF_OVERLAY_MODIFF to BUF_MODIFF. Fold/unfold commands (org-mode, outline-mode,
hideshow-mode) change the 'invisible text property via
`put-text-property', which bumps BUF_MODIFF but not BUF_CHARS_MODIFF.
Using BUF_CHARS_MODIFF would serve stale AX text across fold/unfold.
The rebuild is O(visible-buffer-text) but ensureTextCache is called
exclusively from AX getters at human interaction speed, never from the
redisplay notification path; font-lock passes cause zero rebuild cost.
(postAccessibilityNotificationsForFrame:): Handle BUF_OVERLAY_MODIFF
changes independently of text changes. Use BUF_CHARS_MODIFF to gate changes independently of text changes. Use BUF_CHARS_MODIFF to gate
ValueChanged; keep overlay_modiff out of ensureTextCache to prevent a ValueChanged. Do not call ensureTextCache from the cursor-moved branch:
race where an AX query consumes the change before notification. the granularity detection uses cachedText directly (falling back to
granularity_unknown when the cache is absent), so font-lock passes
cannot trigger O(buffer-size) rebuilds via the notification path.
--- ---
src/nsterm.h | 1 + src/nsterm.h | 1 +
src/nsterm.m | 345 ++++++++++++++++++++++++++++++++++++++++++++------- src/nsterm.m | 358 ++++++++++++++++++++++++++++++++++++++++++++-------
2 files changed, 304 insertions(+), 42 deletions(-) 2 files changed, 316 insertions(+), 43 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h diff --git a/src/nsterm.h b/src/nsterm.h
index f245675513..a210ceba14 100644 index f245675513..a210ceba14 100644
@@ -34,7 +43,7 @@ index f245675513..a210ceba14 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 a0419bb5df..54cee74401 100644 index a0419bb5df..b9d3a0eb53 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 +237,7 @@ index a0419bb5df..54cee74401 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,25 +8149,34 @@ - (void)ensureTextCache @@ -8005,25 +8149,38 @@ - (void)ensureTextCache
if (!b) if (!b)
return; return;
@@ -259,12 +268,16 @@ index a0419bb5df..54cee74401 100644
+ as if it were visible, or miss newly revealed content entirely. + as if it were visible, or miss newly revealed content entirely.
+ +
+ BUF_MODIFF is bumped by all buffer modifications including + BUF_MODIFF is bumped by all buffer modifications including
+ text-property changes (e.g. font-lock face assignments), causing a + text-property changes (e.g. font-lock face assignments). The
+ full text-cache rebuild on each redisplay cycle. This is acceptable + per-rebuild cost is O(visible-buffer-text), but `ensureTextCache'
+ because `ensureTextCache' is only called when VoiceOver queries + is called exclusively from AX getters (accessibilityValue,
+ accessibilityValue or related AX properties --- which happens at + accessibilitySelectedTextRange, etc.) which run at human interaction
+ human interaction speed, not at redisplay speed. The per-rebuild + speed --- not from the redisplay notification path. Font-lock
+ cost is O(visible-buffer-text). + passes do not call this method, so the rebuild cost per font-lock
+ cycle is zero. The redisplay notification path (postAccessibility-
+ NotificationsForFrame:) uses cachedText directly without calling
+ ensureTextCache; granularity detection falls back gracefully when
+ the cache is absent.
+ +
+ Do NOT use BUF_OVERLAY_MODIFF alone: org-mode >= 29 (org-fold-core) + Do NOT use BUF_OVERLAY_MODIFF alone: org-mode >= 29 (org-fold-core)
+ uses text properties, not overlays, for folding, so + uses text properties, not overlays, for folding, so
@@ -280,7 +293,7 @@ index a0419bb5df..54cee74401 100644
&& cachedTextStart == BUF_BEGV (b) && cachedTextStart == BUF_BEGV (b)
&& pt >= cachedTextStart && pt >= cachedTextStart
&& (textLen == 0 && (textLen == 0
@@ -8039,7 +8192,7 @@ included in the cached AX text (it is handled separately via @@ -8039,7 +8196,7 @@ included in the cached AX text (it is handled separately via
{ {
[cachedText release]; [cachedText release];
cachedText = [text retain]; cachedText = [text retain];
@@ -289,7 +302,7 @@ index a0419bb5df..54cee74401 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -8051,9 +8204,9 @@ included in the cached AX text (it is handled separately via @@ -8051,9 +8208,9 @@ included in the cached AX text (it is handled separately via
Walk the cached text once, recording the start offset of each Walk the cached text once, recording the start offset of each
line. Uses NSString lineRangeForRange: --- O(N) in the total line. Uses NSString lineRangeForRange: --- O(N) in the total
text --- but this loop runs only on cache rebuild, which is text --- but this loop runs only on cache rebuild, which is
@@ -302,7 +315,7 @@ index a0419bb5df..54cee74401 100644
enters this code. */ enters this code. */
if (lineStartOffsets) if (lineStartOffsets)
xfree (lineStartOffsets); xfree (lineStartOffsets);
@@ -8108,7 +8261,7 @@ - (NSUInteger)accessibilityIndexForCharpos:(ptrdiff_t)charpos @@ -8108,7 +8265,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
@@ -311,7 +324,7 @@ index a0419bb5df..54cee74401 100644
NSUInteger lo = 0, hi = visibleRunCount; NSUInteger lo = 0, hi = visibleRunCount;
while (lo < hi) while (lo < hi)
{ {
@@ -8157,10 +8310,10 @@ by run length (visible window), not total buffer size. */ @@ -8157,10 +8314,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
@@ -324,7 +337,7 @@ index a0419bb5df..54cee74401 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -8202,7 +8355,7 @@ the slow path (composed character sequence walk), which is @@ -8202,7 +8359,7 @@ the slow path (composed character sequence walk), which is
return cp; return cp;
} }
} }
@@ -333,7 +346,7 @@ index a0419bb5df..54cee74401 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 +8377,7 @@ the slow path (composed character sequence walk), which is @@ -8224,7 +8381,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
@@ -342,7 +355,7 @@ index a0419bb5df..54cee74401 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 +8723,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8570,6 +8727,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -393,7 +406,7 @@ index a0419bb5df..54cee74401 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8792,7 +8989,7 @@ - (NSRect)accessibilityFrame @@ -8792,7 +8993,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -402,7 +415,7 @@ index a0419bb5df..54cee74401 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 +9004,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8807,7 +9008,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -411,7 +424,7 @@ index a0419bb5df..54cee74401 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8826,7 +9023,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8826,7 +9027,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
@@ -420,7 +433,7 @@ index a0419bb5df..54cee74401 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -9220,16 +9417,80 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9220,16 +9421,80 @@ - (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) --- */
@@ -505,7 +518,24 @@ index a0419bb5df..54cee74401 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -12408,7 +12669,7 @@ - (int) fullscreenState @@ -9247,8 +9512,15 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
bool isCtrlNP = ns_ax_event_is_line_nav_key (&ctrlNP);
/* --- Granularity detection --- */
+ /* Use cached text as-is; do NOT call ensureTextCache here.
+ ensureTextCache is O(visible-buffer-text) and must not run on
+ every redisplay cycle. Using stale cached text for granularity
+ classification is safe: the worst case is an incorrect
+ granularity hint (defaulting to unknown), which causes VoiceOver
+ to make its own determination. Fresh text is always available
+ to VoiceOver via the AX getter path (accessibilityValue etc.). */
NSInteger granularity = ns_ax_text_selection_granularity_unknown;
- [self ensureTextCache];
+ BOOL singleLineMove = NO;
if (cachedText && oldPoint > 0)
{
NSUInteger tlen = [cachedText length];
@@ -12408,7 +12680,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -514,7 +544,7 @@ index a0419bb5df..54cee74401 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -12442,7 +12703,7 @@ - (int) fullscreenState @@ -12442,7 +12714,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -523,7 +553,7 @@ index a0419bb5df..54cee74401 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12554,7 +12815,7 @@ - (void)postAccessibilityUpdates @@ -12554,7 +12826,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
@@ -532,7 +562,7 @@ index a0419bb5df..54cee74401 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12563,12 +12824,12 @@ - (void)postAccessibilityUpdates @@ -12563,12 +12835,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 137cb30bb546a9599983c25a9873d1518ad8edee Mon Sep 17 00:00:00 2001 From b54ed57b93cb47250695106021d6e96030ffdd59 Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Mon, 2 Mar 2026 18:49:13 +0100 Date: Mon, 2 Mar 2026 18:49:13 +0100
Subject: [PATCH 9/9] ns: announce child frame completion candidates for Subject: [PATCH 9/9] ns: announce child frame completion candidates for
@@ -37,8 +37,8 @@ Remove Zoom section (covered by patch 0000). Fix dangling paragraph.
doc/emacs/macos.texi | 13 +- doc/emacs/macos.texi | 13 +-
etc/NEWS | 25 +- etc/NEWS | 25 +-
src/nsterm.h | 21 ++ src/nsterm.h | 21 ++
src/nsterm.m | 561 +++++++++++++++++++++++++++++++++++++------ src/nsterm.m | 560 +++++++++++++++++++++++++++++++++++++------
4 files changed, 529 insertions(+), 91 deletions(-) 4 files changed, 528 insertions(+), 91 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 72ac3a9aa9..cf5ed0ff28 100644 index 72ac3a9aa9..cf5ed0ff28 100644
@@ -160,7 +160,7 @@ index a210ceba14..2edd7cd6e0 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 54cee74401..6ba2229639 100644 index b9d3a0eb53..5e48710930 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1275,6 +1275,12 @@ If a completion candidate is selected (overlay or child frame), @@ -1275,6 +1275,12 @@ If a completion candidate is selected (overlay or child frame),
@@ -309,7 +309,7 @@ index 54cee74401..6ba2229639 100644
if (b != current_buffer) if (b != current_buffer)
set_buffer_internal_1 (b); set_buffer_internal_1 (b);
@@ -8605,6 +8726,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range @@ -8609,6 +8730,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range
[self ensureTextCache]; [self ensureTextCache];
@@ -321,7 +321,7 @@ index 54cee74401..6ba2229639 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
@@ -9053,20 +9179,38 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point @@ -9057,20 +9183,38 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point
&& granularity && granularity
== ns_ax_text_selection_granularity_character); == ns_ax_text_selection_granularity_character);
@@ -370,7 +370,7 @@ index 54cee74401..6ba2229639 100644
ns_ax_post_notification_with_info ( ns_ax_post_notification_with_info (
self, self,
NSAccessibilitySelectedTextChangedNotification, NSAccessibilitySelectedTextChangedNotification,
@@ -9166,12 +9310,17 @@ user expectation ("w" jumps to next word and reads it). */ @@ -9170,12 +9314,17 @@ user expectation ("w" jumps to next word and reads it). */
} }
} }
@@ -393,7 +393,7 @@ index 54cee74401..6ba2229639 100644
if (cachedText if (cachedText
&& granularity == ns_ax_text_selection_granularity_line) && granularity == ns_ax_text_selection_granularity_line)
{ {
@@ -9236,6 +9385,11 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9240,6 +9389,11 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
block_input (); block_input ();
specpdl_ref count2 = SPECPDL_INDEX (); specpdl_ref count2 = SPECPDL_INDEX ();
@@ -405,7 +405,7 @@ index 54cee74401..6ba2229639 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)
@@ -9412,12 +9566,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9416,12 +9570,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f
if (!b) if (!b)
return; return;
@@ -435,7 +435,7 @@ index 54cee74401..6ba2229639 100644
if (modiff != self.cachedModiff) if (modiff != self.cachedModiff)
{ {
self.cachedModiff = modiff; self.cachedModiff = modiff;
@@ -9431,6 +9602,7 @@ Text property changes (e.g. face updates from @@ -9435,6 +9606,7 @@ Text property changes (e.g. face updates from
{ {
self.cachedCharsModiff = chars_modiff; self.cachedCharsModiff = chars_modiff;
[self postTextChangedNotification:point]; [self postTextChangedNotification:point];
@@ -443,7 +443,7 @@ index 54cee74401..6ba2229639 100644
} }
} }
@@ -9453,37 +9625,44 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9457,37 +9629,44 @@ 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.
@@ -517,7 +517,7 @@ index 54cee74401..6ba2229639 100644
} }
} }
} }
@@ -9497,7 +9676,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9501,7 +9680,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
self.cachedPoint = point; self.cachedPoint = point;
self.cachedMarkActive = markActive; self.cachedMarkActive = markActive;
@@ -537,15 +537,7 @@ index 54cee74401..6ba2229639 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;
@@ -9509,6 +9699,7 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9534,7 +9724,18 @@ to VoiceOver via the AX getter path (accessibilityValue etc.). */
/* --- Granularity detection --- */
NSInteger granularity = ns_ax_text_selection_granularity_unknown;
+ BOOL singleLineMove = NO;
[self ensureTextCache];
if (cachedText && oldPoint > 0)
{
@@ -9523,7 +9714,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property
NSRange newLine = [cachedText lineRangeForRange: NSRange newLine = [cachedText lineRangeForRange:
NSMakeRange (newIdx, 0)]; NSMakeRange (newIdx, 0)];
if (oldLine.location != newLine.location) if (oldLine.location != newLine.location)
@@ -565,7 +557,7 @@ index 54cee74401..6ba2229639 100644
else else
{ {
NSUInteger dist = (newIdx > oldIdx NSUInteger dist = (newIdx > oldIdx
@@ -9545,38 +9747,23 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property @@ -9556,38 +9757,23 @@ to VoiceOver via the AX getter path (accessibilityValue etc.). */
granularity = ns_ax_text_selection_granularity_line; granularity = ns_ax_text_selection_granularity_line;
} }
@@ -617,7 +609,7 @@ index 54cee74401..6ba2229639 100644
{ {
NSWindow *win = [self.emacsView window]; NSWindow *win = [self.emacsView window];
if (win) if (win)
@@ -9735,6 +9922,13 @@ - (NSRect)accessibilityFrame @@ -9746,6 +9932,13 @@ - (NSRect)accessibilityFrame
if (vis_start >= vis_end) if (vis_start >= vis_end)
return @[]; return @[];
@@ -631,7 +623,7 @@ index 54cee74401..6ba2229639 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);
@@ -10042,6 +10236,10 @@ - (void)dealloc @@ -10053,6 +10246,10 @@ - (void)dealloc
#endif #endif
[accessibilityElements release]; [accessibilityElements release];
@@ -642,7 +634,7 @@ index 54cee74401..6ba2229639 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11491,6 +11689,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f @@ -11502,6 +11699,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO; windowClosing = NO;
processingCompose = NO; processingCompose = NO;
@@ -652,7 +644,7 @@ index 54cee74401..6ba2229639 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;
@@ -12799,6 +13000,156 @@ - (id)accessibilityFocusedUIElement @@ -12810,6 +13010,156 @@ - (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. */
@@ -809,7 +801,7 @@ index 54cee74401..6ba2229639 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12809,11 +13160,69 @@ - (void)postAccessibilityUpdates @@ -12820,11 +13170,69 @@ - (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