patches: fix O(position) lag — O(1) fast path in charposForAccessibilityIndex:

charposForAccessibilityIndex: walked composed character sequences
from the start of a visible run to the target AX index.  For a run
covering an entire ASCII buffer, this is O(cursor_position): moving
to line 10,000 requires ~500,000 iterations per call.

The method is called on every SelectedTextChanged notification
response (accessibilityBoundsForRange: from the AX server for cursor
tracking), making cursor movement O(position) in large files.

Fix: when ax_length == length for a run (all characters are single
AX index units — true for all ASCII/Latin text), the charpos offset
is simply ax_idx - run.ax_start.  O(1) instead of O(position).

Multi-byte runs (emoji, CJK, non-BMP) fall back to the sequence walk,
bounded by run length (visible window size), not total buffer size.
This commit is contained in:
2026-03-01 09:03:01 +01:00
parent 31ad038360
commit fb68dd50ea
9 changed files with 73 additions and 66 deletions

View File

@@ -1,7 +1,7 @@
From aeab7e4db306049fb543f60cadcaaa346350f77b Mon Sep 17 00:00:00 2001 From 03a3e77f9ff5f46429964863a2f320e119c0686c 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 1/9] ns: integrate with macOS Zoom for cursor tracking Subject: [PATCH 0/8] ns: integrate with macOS Zoom for cursor tracking
Inform macOS Zoom of the text cursor position so the zoomed viewport Inform macOS Zoom of the text cursor position so the zoomed viewport
follows keyboard focus in Emacs. follows keyboard focus in Emacs.

View File

@@ -1,7 +1,7 @@
From 60516534a246d6ac5b6c91c1e673785943c65fe1 Mon Sep 17 00:00:00 2001 From 23f582e52ede92fb6d04bfd0062557757bea0971 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/9] ns: add accessibility base classes and text extraction Subject: [PATCH 1/8] ns: add accessibility base classes and text extraction
Add the foundation for macOS VoiceOver accessibility in the NS Add the foundation for macOS VoiceOver accessibility in the NS
(Cocoa) port. No existing code paths are modified. (Cocoa) port. No existing code paths are modified.

View File

@@ -1,7 +1,7 @@
From a75cbfb2c856c834a16d60cad2ada4ad4487bbb9 Mon Sep 17 00:00:00 2001 From 1b45f1f5ae3fe88836c8bfe60233e9a078a50641 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/9] ns: implement buffer accessibility element (core Subject: [PATCH 2/8] ns: implement buffer accessibility element (core
protocol) protocol)
Implement the NSAccessibility text protocol for Emacs buffer windows. Implement the NSAccessibility text protocol for Emacs buffer windows.
@@ -18,14 +18,14 @@ setAccessibilityFocused.
Tested on macOS 14 with VoiceOver. Verified: buffer reading, Tested on macOS 14 with VoiceOver. Verified: buffer reading,
line-by-line navigation, word/character announcements. line-by-line navigation, word/character announcements.
--- ---
src/nsterm.m | 1104 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/nsterm.m | 1112 ++++++++++++++++++++++++++++++++++++++++++++++++++
1 file changed, 1104 insertions(+) 1 file changed, 1112 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index e516946..8f528bd 100644 index e516946..4d54940 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7614,6 +7614,1110 @@ - (id)accessibilityTopLevelUIElement @@ -7614,6 +7614,1118 @@ - (id)accessibilityTopLevelUIElement
@end @end
@@ -521,8 +521,16 @@ index e516946..8f528bd 100644
+ lo = mid + 1; + lo = mid + 1;
+ else + else
+ { + {
+ /* Found: ax_idx is inside this run. Walk composed character + /* Found: ax_idx is inside this run.
+ sequences to count Emacs characters up to ax_idx. */ + Fast path for pure-ASCII runs: ax_length == length means
+ every Emacs charpos maps to exactly one AX string index.
+ The conversion is then O(1) instead of O(cursor_position).
+ Buffers with emoji, CJK, or other non-BMP characters use
+ the slow path (composed character sequence walk), which is
+ bounded by run length, not total buffer size. */
+ if (r->ax_length == (NSUInteger) r->length)
+ return r->charpos + (ptrdiff_t) (ax_idx - r->ax_start);
+
+ if (!cachedText) + if (!cachedText)
+ return r->charpos; + return r->charpos;
+ NSUInteger scan = r->ax_start; + NSUInteger scan = r->ax_start;

View File

@@ -1,7 +1,7 @@
From 04dfd354788b32dc81dd57b61fb5494b0f84a817 Mon Sep 17 00:00:00 2001 From 545547dca37282a5a9bc858e0ccac724aa87b3d8 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/9] ns: add buffer notification dispatch and mode-line Subject: [PATCH 3/8] ns: add buffer notification dispatch and mode-line
element element
Add VoiceOver notification methods and mode-line readout. Add VoiceOver notification methods and mode-line readout.
@@ -24,10 +24,10 @@ region selection feedback, completion popups, mode-line reading.
1 file changed, 545 insertions(+) 1 file changed, 545 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 8f528bd..7e3d57a 100644 index 4d54940..a73b7f5 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -8718,6 +8718,552 @@ - (NSRect)accessibilityFrame @@ -8726,6 +8726,551 @@ - (NSRect)accessibilityFrame
@end @end
@@ -432,18 +432,17 @@ index 8f528bd..7e3d57a 100644
+ [self ensureTextCache]; + [self ensureTextCache];
+ if (cachedText && oldPoint > 0) + if (cachedText && oldPoint > 0)
+ { + {
+ NSUInteger tlen = [cachedText length];
+ NSUInteger oldIdx = [self accessibilityIndexForCharpos:oldPoint]; + NSUInteger oldIdx = [self accessibilityIndexForCharpos:oldPoint];
+ NSUInteger newIdx = [self accessibilityIndexForCharpos:point]; + NSUInteger newIdx = [self accessibilityIndexForCharpos:point];
+ if (oldIdx > tlen) oldIdx = tlen;
+ if (newIdx > tlen) newIdx = tlen;
+ +
+ /* Use precomputed lineStartOffsets for O(log L) line lookup. + NSRange oldLine = [cachedText lineRangeForRange:
+ NSString lineRangeForRange: is O(offset) — it scans from + NSMakeRange (oldIdx, 0)];
+ the start of the string, causing progressive slowdown in + NSRange newLine = [cachedText lineRangeForRange:
+ large buffers (O(cursor_position) per redisplay cycle). + NSMakeRange (newIdx, 0)];
+ lineForAXIndex: binary-searches lineStartOffsets, built + if (oldLine.location != newLine.location)
+ once per cache rebuild in ensureTextCache. */
+ NSInteger oldLine = [self lineForAXIndex: oldIdx];
+ NSInteger newLine = [self lineForAXIndex: newIdx];
+ if (oldLine != newLine)
+ granularity = ns_ax_text_selection_granularity_line; + granularity = ns_ax_text_selection_granularity_line;
+ else + else
+ { + {

View File

@@ -1,7 +1,7 @@
From 7303dd3913729a454b44fe71219acbd612d624b4 Mon Sep 17 00:00:00 2001 From a1c42c0fefc4cc942884c0fe44520717742e4dba 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/9] ns: add interactive span elements for Tab navigation Subject: [PATCH 4/8] ns: add interactive span elements for Tab navigation
* src/nsterm.m (ns_ax_scan_interactive_spans): New function. * src/nsterm.m (ns_ax_scan_interactive_spans): New function.
(EmacsAccessibilityInteractiveSpan): Implement AXButton/AXLink (EmacsAccessibilityInteractiveSpan): Implement AXButton/AXLink
@@ -17,10 +17,10 @@ Tested on macOS 14. Verified: Tab-cycling through org-mode links,
1 file changed, 286 insertions(+) 1 file changed, 286 insertions(+)
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 7e3d57a..1a21f2e 100644 index a73b7f5..98ff027 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -9263,6 +9263,292 @@ - (NSRect)accessibilityFrame @@ -9271,6 +9271,292 @@ - (NSRect)accessibilityFrame
@end @end

View File

@@ -1,7 +1,7 @@
From 4a81923ad68bc5866f7343b37429a882f7a388dd Mon Sep 17 00:00:00 2001 From ae1c7d6451f5a6be397f50c314e03b43b4e47b5c 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/9] ns: integrate accessibility with EmacsView and redisplay Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay
Wire the accessibility infrastructure into EmacsView and the Wire the accessibility infrastructure into EmacsView and the
@@ -51,7 +51,7 @@ index 80661a9..2b1f9e6 100644
** Re-introduced dictation, lost in Emacs v30 (macOS). ** Re-introduced dictation, lost in Emacs v30 (macOS).
We lost macOS dictation in v30 when migrating to NSTextInputClient. We lost macOS dictation in v30 when migrating to NSTextInputClient.
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 1a21f2e..f8b7a8d 100644 index 98ff027..0a70f3e 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -1258,7 +1258,7 @@ If a completion candidate is selected (overlay or child frame), @@ -1258,7 +1258,7 @@ If a completion candidate is selected (overlay or child frame),
@@ -157,7 +157,7 @@ index 1a21f2e..f8b7a8d 100644
static BOOL static BOOL
ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point, ns_ax_find_completion_overlay_range (struct buffer *b, ptrdiff_t point,
ptrdiff_t *out_start, ptrdiff_t *out_start,
@@ -8719,7 +8769,6 @@ - (NSRect)accessibilityFrame @@ -8727,7 +8777,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -165,7 +165,7 @@ index 1a21f2e..f8b7a8d 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityBuffer (Notifications) — AX event dispatch EmacsAccessibilityBuffer (Notifications) — AX event dispatch
@@ -9264,7 +9313,6 @@ - (NSRect)accessibilityFrame @@ -9272,7 +9321,6 @@ - (NSRect)accessibilityFrame
@end @end
@@ -173,7 +173,7 @@ index 1a21f2e..f8b7a8d 100644
/* =================================================================== /* ===================================================================
EmacsAccessibilityInteractiveSpan — helpers and implementation EmacsAccessibilityInteractiveSpan — helpers and implementation
=================================================================== */ =================================================================== */
@@ -9594,6 +9642,7 @@ - (void)dealloc @@ -9602,6 +9650,7 @@ - (void)dealloc
[layer release]; [layer release];
#endif #endif
@@ -181,7 +181,7 @@ index 1a21f2e..f8b7a8d 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -10942,6 +10991,32 @@ - (void)windowDidBecomeKey /* for direct calls */ @@ -10950,6 +10999,32 @@ - (void)windowDidBecomeKey /* for direct calls */
XSETFRAME (event.frame_or_window, emacsframe); XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event); kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop ns_send_appdefined (-1); // Kick main loop
@@ -214,7 +214,7 @@ index 1a21f2e..f8b7a8d 100644
} }
@@ -12179,6 +12254,332 @@ - (int) fullscreenState @@ -12187,6 +12262,332 @@ - (int) fullscreenState
return fs_state; return fs_state;
} }
@@ -547,7 +547,7 @@ index 1a21f2e..f8b7a8d 100644
@end /* EmacsView */ @end /* EmacsView */
@@ -14179,12 +14580,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with @@ -14187,12 +14588,17 @@ Nil means use fullscreen the old (< 10.7) way. The old way works better with
ns_use_srgb_colorspace = YES; ns_use_srgb_colorspace = YES;
DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled, DEFVAR_BOOL ("ns-accessibility-enabled", ns_accessibility_enabled,

View File

@@ -1,7 +1,7 @@
From ca89e85f199e054a6cef6272425d2920e55b2ea5 Mon Sep 17 00:00:00 2001 From 4341052ad931a98b8453368753b596e7743132b8 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 7/9] doc: add VoiceOver accessibility section to macOS Subject: [PATCH 6/8] doc: add VoiceOver accessibility section to macOS
appendix appendix
* doc/emacs/macos.texi (VoiceOver Accessibility): New node. Document * doc/emacs/macos.texi (VoiceOver Accessibility): New node. Document

View File

@@ -1,7 +1,7 @@
From 0e5f90b02aa9d9b9ba1fdba7d88ae54e17417fe9 Mon Sep 17 00:00:00 2001 From 8dd1a4cd6d3f58a3c6f9454ba1690a442c2048fe Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 14:46:25 +0100 Date: Sat, 28 Feb 2026 14:46:25 +0100
Subject: [PATCH 8/9] ns: announce overlay completion candidates for VoiceOver Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver
Completion frameworks such as Vertico, Ivy, and Icomplete render Completion frameworks such as Vertico, Ivy, and Icomplete render
candidates via overlay before-string/after-string properties rather candidates via overlay before-string/after-string properties rather
@@ -61,7 +61,7 @@ index 6e830de..2102fb9 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 f8b7a8d..3e005e0 100644 index 0a70f3e..c74eaf1 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7254,11 +7254,154 @@ Accessibility virtual elements (macOS / Cocoa only) @@ -7254,11 +7254,154 @@ Accessibility virtual elements (macOS / Cocoa only)
@@ -329,7 +329,7 @@ index f8b7a8d..3e005e0 100644
@synchronized (self) @synchronized (self)
{ {
if (visibleRunCount == 0) if (visibleRunCount == 0)
@@ -8172,7 +8307,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -8180,7 +8315,7 @@ the slow path (composed character sequence walk), which is
return cp; return cp;
} }
} }
@@ -338,7 +338,7 @@ index f8b7a8d..3e005e0 100644
if (lo > 0) if (lo > 0)
{ {
ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1]; ns_ax_visible_run *last = &visibleRuns[visibleRunCount - 1];
@@ -8194,7 +8329,7 @@ - (ptrdiff_t)charposForAccessibilityIndex:(NSUInteger)ax_idx @@ -8202,7 +8337,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
@@ -347,7 +347,7 @@ index f8b7a8d..3e005e0 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
@@ -8548,6 +8683,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber @@ -8556,6 +8691,50 @@ - (NSInteger)accessibilityInsertionPointLineNumber
return [self lineForAXIndex:point_idx]; return [self lineForAXIndex:point_idx];
} }
@@ -398,7 +398,7 @@ index f8b7a8d..3e005e0 100644
- (NSRange)accessibilityRangeForLine:(NSInteger)line - (NSRange)accessibilityRangeForLine:(NSInteger)line
{ {
if (![NSThread isMainThread]) if (![NSThread isMainThread])
@@ -8770,7 +8949,7 @@ - (NSRect)accessibilityFrame @@ -8778,7 +8957,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -407,7 +407,7 @@ index f8b7a8d..3e005e0 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).
@@ -8785,7 +8964,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8793,7 +8972,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point
if (point > self.cachedPoint if (point > self.cachedPoint
&& point - self.cachedPoint == 1) && point - self.cachedPoint == 1)
{ {
@@ -416,7 +416,7 @@ index f8b7a8d..3e005e0 100644
[self invalidateTextCache]; [self invalidateTextCache];
[self ensureTextCache]; [self ensureTextCache];
if (cachedText) if (cachedText)
@@ -8804,7 +8983,7 @@ - (void)postTextChangedNotification:(ptrdiff_t)point @@ -8812,7 +8991,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
@@ -425,7 +425,7 @@ index f8b7a8d..3e005e0 100644
self.cachedPoint = point; self.cachedPoint = point;
NSDictionary *change = @{ NSDictionary *change = @{
@@ -9137,16 +9316,83 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f @@ -9145,16 +9324,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) --- */
@@ -513,7 +513,7 @@ index f8b7a8d..3e005e0 100644
{ {
ptrdiff_t oldPoint = self.cachedPoint; ptrdiff_t oldPoint = self.cachedPoint;
BOOL oldMarkActive = self.cachedMarkActive; BOOL oldMarkActive = self.cachedMarkActive;
@@ -9314,7 +9560,7 @@ - (NSRect)accessibilityFrame @@ -9322,7 +9568,7 @@ - (NSRect)accessibilityFrame
/* =================================================================== /* ===================================================================
@@ -522,7 +522,7 @@ index f8b7a8d..3e005e0 100644
=================================================================== */ =================================================================== */
/* Scan visible range of window W for interactive spans. /* Scan visible range of window W for interactive spans.
@@ -9522,7 +9768,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9530,7 +9776,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
dispatch_async (dispatch_get_main_queue (), ^{ dispatch_async (dispatch_get_main_queue (), ^{
/* lwin is a Lisp_Object captured by value. This is GC-safe /* lwin is a Lisp_Object captured by value. This is GC-safe
because Lisp_Objects are tagged integers/pointers that because Lisp_Objects are tagged integers/pointers that
@@ -531,7 +531,7 @@ index f8b7a8d..3e005e0 100644
Emacs. The WINDOW_LIVE_P check below guards against the Emacs. The WINDOW_LIVE_P check below guards against the
window being deleted between capture and execution. */ window being deleted between capture and execution. */
if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin))) if (!WINDOWP (lwin) || NILP (Fwindow_live_p (lwin)))
@@ -9548,7 +9794,7 @@ - (void) setAccessibilityFocused: (BOOL) focused @@ -9556,7 +9802,7 @@ - (void) setAccessibilityFocused: (BOOL) focused
@end @end
@@ -540,7 +540,7 @@ index f8b7a8d..3e005e0 100644
Methods are kept here (same .m file) so they access the ivars Methods are kept here (same .m file) so they access the ivars
declared in the @interface ivar block. */ declared in the @interface ivar block. */
@implementation EmacsAccessibilityBuffer (InteractiveSpans) @implementation EmacsAccessibilityBuffer (InteractiveSpans)
@@ -12270,7 +12516,7 @@ - (int) fullscreenState @@ -12278,7 +12524,7 @@ - (int) fullscreenState
if (WINDOW_LEAF_P (w)) if (WINDOW_LEAF_P (w))
{ {
@@ -549,7 +549,7 @@ index f8b7a8d..3e005e0 100644
EmacsAccessibilityBuffer *elem EmacsAccessibilityBuffer *elem
= [existing objectForKey:[NSValue valueWithPointer:w]]; = [existing objectForKey:[NSValue valueWithPointer:w]];
if (!elem) if (!elem)
@@ -12304,7 +12550,7 @@ - (int) fullscreenState @@ -12312,7 +12558,7 @@ - (int) fullscreenState
} }
else else
{ {
@@ -558,7 +558,7 @@ index f8b7a8d..3e005e0 100644
Lisp_Object child = w->contents; Lisp_Object child = w->contents;
while (!NILP (child)) while (!NILP (child))
{ {
@@ -12416,7 +12662,7 @@ - (void)postAccessibilityUpdates @@ -12424,7 +12670,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
@@ -567,7 +567,7 @@ index f8b7a8d..3e005e0 100644
Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe); Lisp_Object curRoot = FRAME_ROOT_WINDOW (emacsframe);
if (!EQ (curRoot, lastRootWindow)) if (!EQ (curRoot, lastRootWindow))
{ {
@@ -12425,12 +12671,12 @@ - (void)postAccessibilityUpdates @@ -12433,12 +12679,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,7 +1,7 @@
From 85259296769fe8e30d3fa2d8a7626729023c8691 Mon Sep 17 00:00:00 2001 From 35a32f4802822ee77350a6d652aa45cca38e304c Mon Sep 17 00:00:00 2001
From: Martin Sukany <martin@sukany.cz> From: Martin Sukany <martin@sukany.cz>
Date: Sat, 28 Feb 2026 16:01:29 +0100 Date: Sat, 28 Feb 2026 16:01:29 +0100
Subject: [PATCH 9/9] ns: announce child frame completion candidates for Subject: [PATCH 8/8] ns: announce child frame completion candidates for
VoiceOver VoiceOver
Completion frameworks such as Corfu, Company-box, and similar Completion frameworks such as Corfu, Company-box, and similar
@@ -109,7 +109,7 @@ index 2102fb9..dd98d56 100644
@end @end
diff --git a/src/nsterm.m b/src/nsterm.m diff --git a/src/nsterm.m b/src/nsterm.m
index 3e005e0..d61a634 100644 index c74eaf1..d2f88a5 100644
--- a/src/nsterm.m --- a/src/nsterm.m
+++ b/src/nsterm.m +++ b/src/nsterm.m
@@ -7398,6 +7398,112 @@ visual line index for Zoom (skip whitespace-only lines @@ -7398,6 +7398,112 @@ visual line index for Zoom (skip whitespace-only lines
@@ -268,7 +268,7 @@ index 3e005e0..d61a634 100644
cachedTextStart = start; cachedTextStart = start;
if (visibleRuns) if (visibleRuns)
@@ -9135,6 +9250,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9143,6 +9258,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
ptrdiff_t currentOverlayStart = 0; ptrdiff_t currentOverlayStart = 0;
ptrdiff_t currentOverlayEnd = 0; ptrdiff_t currentOverlayEnd = 0;
@@ -276,7 +276,7 @@ index 3e005e0..d61a634 100644
specpdl_ref count2 = SPECPDL_INDEX (); specpdl_ref count2 = SPECPDL_INDEX ();
record_unwind_current_buffer (); record_unwind_current_buffer ();
if (b != current_buffer) if (b != current_buffer)
@@ -9293,6 +9409,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b @@ -9301,6 +9417,7 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b
self.cachedCompletionOverlayEnd = 0; self.cachedCompletionOverlayEnd = 0;
self.cachedCompletionPoint = 0; self.cachedCompletionPoint = 0;
} }
@@ -284,7 +284,7 @@ index 3e005e0..d61a634 100644
} }
/* ---- Notification dispatch (main entry point) ---- */ /* ---- Notification dispatch (main entry point) ---- */
@@ -9889,6 +10006,10 @@ - (void)dealloc @@ -9897,6 +10014,10 @@ - (void)dealloc
#endif #endif
[accessibilityElements release]; [accessibilityElements release];
@@ -295,7 +295,7 @@ index 3e005e0..d61a634 100644
[[self menu] release]; [[self menu] release];
[super dealloc]; [super dealloc];
} }
@@ -11338,6 +11459,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f @@ -11346,6 +11467,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f
windowClosing = NO; windowClosing = NO;
processingCompose = NO; processingCompose = NO;
@@ -305,7 +305,7 @@ index 3e005e0..d61a634 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;
@@ -12646,6 +12770,80 @@ - (id)accessibilityFocusedUIElement @@ -12654,6 +12778,80 @@ - (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. */
@@ -386,7 +386,7 @@ index 3e005e0..d61a634 100644
- (void)postAccessibilityUpdates - (void)postAccessibilityUpdates
{ {
NSTRACE ("[EmacsView postAccessibilityUpdates]"); NSTRACE ("[EmacsView postAccessibilityUpdates]");
@@ -12656,11 +12854,59 @@ - (void)postAccessibilityUpdates @@ -12664,11 +12862,59 @@ - (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