From 9bee2a19873bb2ddce1c1bdb48ad779899126662 Mon Sep 17 00:00:00 2001 From: Daneel Date: Tue, 3 Mar 2026 10:21:41 +0100 Subject: [PATCH] patches: address remaining medium/low review findings M-REM-1 - Stack array bound comment: ns_ax_selected_child_frame_text uses line_starts/line_ends[128]. Added comment documenting the 128-line bound and the 10,000-character upstream guard that makes silent truncation safe in practice. M-REM-2 - Remove dead BUFFER_LIVE_P check: The second BUFFER_LIVE_P check in announceChildFrameCompletion (after updating childFrameLastBuffer/Modiff) is unreachable: no code between the entry check and this point can kill the buffer. Replaced with eassert so DEBUG builds catch violations while releasing dead code. M-REM-3 - @interface declaration for postEchoAreaAnnouncementIfNeeded: Added declaration to the #ifdef NS_IMPL_COCOA accessibility block in nsterm.h alongside existing postAccessibilityUpdates and announceChildFrameCompletion declarations. postAccessibilityUpdates is called from ns_update_end; all three methods should be declared. L-REM-4 - Commit message class name: Changed (EmacsAXBuffer) to (EmacsAccessibilityBuffer) in the subject line. EmacsAXBuffer does not exist; EmacsAccessibilityBuffer is the actual class that received the voiceoverSetPoint ivar. NEWS - Remove third-party package names: Replaced 'Vertico, Icomplete, Ivy for overlay candidates; Corfu, Company-box for child frame popups' with generic descriptions per GNU Emacs NEWS convention (third-party package names avoided in NEWS). --- ...-with-macOS-Zoom-for-cursor-tracking.patch | 2 +- ...lity-base-classes-and-text-extractio.patch | 2 +- ...fer-accessibility-element-core-proto.patch | 2 +- ...tification-dispatch-and-mode-line-el.patch | 2 +- ...ive-span-elements-for-Tab-navigation.patch | 2 +- ...essibility-with-EmacsView-and-redisp.patch | 2 +- ...r-accessibility-section-to-macOS-app.patch | 2 +- ...lay-completion-candidates-for-VoiceO.patch | 2 +- ...d-frame-completion-candidates-for-Vo.patch | 88 ++++++++++++------- 9 files changed, 63 insertions(+), 41 deletions(-) diff --git a/patches/0000-ns-integrate-with-macOS-Zoom-for-cursor-tracking.patch b/patches/0000-ns-integrate-with-macOS-Zoom-for-cursor-tracking.patch index 46bcfce..c9d78e9 100644 --- a/patches/0000-ns-integrate-with-macOS-Zoom-for-cursor-tracking.patch +++ b/patches/0000-ns-integrate-with-macOS-Zoom-for-cursor-tracking.patch @@ -1,4 +1,4 @@ -From 30d31b473d43ff800f4f8d21f913c9e50acc6ad3 Mon Sep 17 00:00:00 2001 +From 0470786c91eb4a464d8580387b83a4a8d4e4f8eb Mon Sep 17 00:00:00 2001 From: Martin Sukany Date: Sat, 28 Feb 2026 22:39:35 +0100 Subject: [PATCH 0/8] ns: integrate with macOS Zoom for cursor tracking diff --git a/patches/0001-ns-add-accessibility-base-classes-and-text-extractio.patch b/patches/0001-ns-add-accessibility-base-classes-and-text-extractio.patch index 8015ac0..1acec93 100644 --- a/patches/0001-ns-add-accessibility-base-classes-and-text-extractio.patch +++ b/patches/0001-ns-add-accessibility-base-classes-and-text-extractio.patch @@ -1,4 +1,4 @@ -From 5a6d1df78087140f8808e7b534b0a6752b5b1566 Mon Sep 17 00:00:00 2001 +From 6c075e29fccc7dc6e9df693c4123ce0001a2dbfc Mon Sep 17 00:00:00 2001 From: Martin Sukany Date: Sat, 28 Feb 2026 12:58:11 +0100 Subject: [PATCH 1/8] ns: add accessibility base classes and text extraction diff --git a/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch b/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch index 7c7c0c1..09ee937 100644 --- a/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch +++ b/patches/0002-ns-implement-buffer-accessibility-element-core-proto.patch @@ -1,4 +1,4 @@ -From d7fdf41e480f65022dac11742e957ed6541e1cd1 Mon Sep 17 00:00:00 2001 +From 705bba0809db081f538c124aa900bc911de9b0ff Mon Sep 17 00:00:00 2001 From: Martin Sukany Date: Sat, 28 Feb 2026 12:58:11 +0100 Subject: [PATCH 2/8] ns: implement buffer accessibility element (core diff --git a/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch b/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch index b71851b..815f5df 100644 --- a/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch +++ b/patches/0003-ns-add-buffer-notification-dispatch-and-mode-line-el.patch @@ -1,4 +1,4 @@ -From 22968029ca303f3b472e4729675d5d7e542e2817 Mon Sep 17 00:00:00 2001 +From 94d2aa2736ec116e2965d02241ad8b20d8daf4bc Mon Sep 17 00:00:00 2001 From: Martin Sukany Date: Sat, 28 Feb 2026 12:58:11 +0100 Subject: [PATCH 3/8] ns: add buffer notification dispatch and mode-line diff --git a/patches/0004-ns-add-interactive-span-elements-for-Tab-navigation.patch b/patches/0004-ns-add-interactive-span-elements-for-Tab-navigation.patch index b395755..193f852 100644 --- a/patches/0004-ns-add-interactive-span-elements-for-Tab-navigation.patch +++ b/patches/0004-ns-add-interactive-span-elements-for-Tab-navigation.patch @@ -1,4 +1,4 @@ -From 6a4b3c33a0d9fdced519aff87cc02da13763f17c Mon Sep 17 00:00:00 2001 +From 3206d93511fe9337c4ca683a5dc1e6885ed9985c Mon Sep 17 00:00:00 2001 From: Martin Sukany Date: Sat, 28 Feb 2026 12:58:11 +0100 Subject: [PATCH 4/8] ns: add interactive span elements for Tab navigation diff --git a/patches/0005-ns-integrate-accessibility-with-EmacsView-and-redisp.patch b/patches/0005-ns-integrate-accessibility-with-EmacsView-and-redisp.patch index fef0170..55f68ea 100644 --- a/patches/0005-ns-integrate-accessibility-with-EmacsView-and-redisp.patch +++ b/patches/0005-ns-integrate-accessibility-with-EmacsView-and-redisp.patch @@ -1,4 +1,4 @@ -From 3a01e0c261a7ece2e8330dce32d4be6d5036be13 Mon Sep 17 00:00:00 2001 +From f2e97ea6ba4ffc1c73e625f9d61636b7261cbecf Mon Sep 17 00:00:00 2001 From: Martin Sukany Date: Sat, 28 Feb 2026 12:58:11 +0100 Subject: [PATCH 5/8] ns: integrate accessibility with EmacsView and redisplay diff --git a/patches/0006-doc-add-VoiceOver-accessibility-section-to-macOS-app.patch b/patches/0006-doc-add-VoiceOver-accessibility-section-to-macOS-app.patch index 42330ff..e6bda59 100644 --- a/patches/0006-doc-add-VoiceOver-accessibility-section-to-macOS-app.patch +++ b/patches/0006-doc-add-VoiceOver-accessibility-section-to-macOS-app.patch @@ -1,4 +1,4 @@ -From 7578a7b5e33557c7e34674cd0bf3b7cf627898e6 Mon Sep 17 00:00:00 2001 +From 4310549aa1e486dba054948a2937bb8bb236bb27 Mon Sep 17 00:00:00 2001 From: Martin Sukany Date: Sat, 28 Feb 2026 12:58:11 +0100 Subject: [PATCH 6/8] doc: add VoiceOver accessibility section to macOS diff --git a/patches/0007-ns-announce-overlay-completion-candidates-for-VoiceO.patch b/patches/0007-ns-announce-overlay-completion-candidates-for-VoiceO.patch index 5406103..1b4e8f7 100644 --- a/patches/0007-ns-announce-overlay-completion-candidates-for-VoiceO.patch +++ b/patches/0007-ns-announce-overlay-completion-candidates-for-VoiceO.patch @@ -1,4 +1,4 @@ -From b6f4bb85d337a22bd6e761181aa0f192d5d078f8 Mon Sep 17 00:00:00 2001 +From affdaf60d28ad4d9836c6505216e19599b31c437 Mon Sep 17 00:00:00 2001 From: Daneel Date: Mon, 2 Mar 2026 18:39:46 +0100 Subject: [PATCH 7/8] ns: announce overlay completion candidates for VoiceOver diff --git a/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch b/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch index 80abae3..29af831 100644 --- a/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch +++ b/patches/0008-ns-announce-child-frame-completion-candidates-for-Vo.patch @@ -1,4 +1,4 @@ -From 7cb9bca680a56848fe4bd4ddb51f643e8946e7b8 Mon Sep 17 00:00:00 2001 +From 1e3d3919fd41e4480a02190fb89bee1ef8107d62 Mon Sep 17 00:00:00 2001 From: Daneel Date: Mon, 2 Mar 2026 18:49:13 +0100 Subject: [PATCH 8/8] ns: announce child frame completion candidates for @@ -14,7 +14,7 @@ childFrameLastCandidate, childFrameCompletionActive, lastEchoCharsModiff ivars; remove cachedOverlayModiffForText (unused after BUF_OVERLAY_MODIFF removed from ensureTextCache to prevent hl-line-mode O(N) rebuilds). Initialize voiceoverSetPoint, childFrameLastBuffer in initFrameFromEmacs:. -(EmacsAXBuffer): Add voiceoverSetPoint ivar. +(EmacsAccessibilityBuffer): Add voiceoverSetPoint ivar. * src/nsterm.m (ns_ax_buffer_text): Add block_input protection for Lisp calls; use record_unwind_protect_void to guarantee unblock_input. (ensureTextCache): Remove BUF_OVERLAY_MODIFF tracking; keep only @@ -31,10 +31,10 @@ area announcements. * doc/emacs/macos.texi: Fix dangling semicolon in GNUstep paragraph. --- doc/emacs/macos.texi | 14 +- - etc/NEWS | 18 +- - src/nsterm.h | 20 ++ - src/nsterm.m | 507 +++++++++++++++++++++++++++++++++++++++---- - 4 files changed, 499 insertions(+), 60 deletions(-) + etc/NEWS | 25 ++- + src/nsterm.h | 21 ++ + src/nsterm.m | 514 +++++++++++++++++++++++++++++++++++++++---- + 4 files changed, 510 insertions(+), 64 deletions(-) diff --git a/doc/emacs/macos.texi b/doc/emacs/macos.texi index 8d4a7825d8..03a657f970 100644 @@ -69,10 +69,24 @@ index 8d4a7825d8..03a657f970 100644 @vindex ns-accessibility-enabled To disable the accessibility interface entirely (for instance, to diff --git a/etc/NEWS b/etc/NEWS -index 7f917f93b2..d7631fa6c7 100644 +index 7f917f93b2..bbec21b635 100644 --- a/etc/NEWS +++ b/etc/NEWS -@@ -4385,16 +4385,20 @@ allowing Emacs users access to speech recognition utilities. +@@ -88,10 +88,9 @@ When macOS Zoom is enabled (System Settings, Accessibility, Zoom, + Follow keyboard focus), Emacs informs Zoom of the text cursor position + after every cursor redraw via 'UAZoomChangeFocus'. The zoomed viewport + automatically tracks the insertion point across window splits and +-switches. Completion frameworks (Vertico, Icomplete, Ivy for overlay +-candidates; Corfu, Company-box for child frame popups) are also +-tracked: Zoom follows the selected candidate rather than the text +-cursor during completion. ++switches. Overlay-based completion frameworks and child-frame popup completions ++are also tracked: Zoom follows the selected candidate rather than the ++text cursor during completion. + + +++ + ** 'line-spacing' now supports specifying spacing above the line. +@@ -4385,16 +4384,20 @@ allowing Emacs users access to speech recognition utilities. Note: Accepting this permission allows the use of system APIs, which may send user data to Apple's speech recognition servers. @@ -95,13 +109,13 @@ index 7f917f93b2..d7631fa6c7 100644 + jumps (]], M-<, xref, imenu, etc.). +- Tab-navigable interactive spans (buttons, links, completion + candidates) within a buffer. -+- Completion announcements for the *Completions* buffer and overlay -+ and child-frame completion UIs (Vertico, Corfu, Company-box). ++- Completion announcements for the *Completions* buffer, overlay ++ completion UIs, and child-frame completion popup UIs. Set 'ns-accessibility-enabled' to nil to disable the accessibility interface and eliminate the associated overhead. diff --git a/src/nsterm.h b/src/nsterm.h -index 21a93bc799..bdd40b8eb7 100644 +index 21a93bc799..b5c9f84499 100644 --- a/src/nsterm.h +++ b/src/nsterm.h @@ -504,9 +504,20 @@ typedef struct ns_ax_visible_run @@ -140,16 +154,17 @@ index 21a93bc799..bdd40b8eb7 100644 #endif BOOL font_panel_active; NSFont *font_panel_result; -@@ -665,6 +684,7 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) +@@ -665,6 +684,8 @@ typedef NS_ENUM (NSInteger, EmacsAXSpanType) - (void)rebuildAccessibilityTree; - (void)invalidateAccessibilityTree; - (void)postAccessibilityUpdates; ++- (void)postEchoAreaAnnouncementIfNeeded; +- (void)announceChildFrameCompletion; #endif @end diff --git a/src/nsterm.m b/src/nsterm.m -index 8ef344d9fe..5038f9830d 100644 +index 8ef344d9fe..1acb64630a 100644 --- a/src/nsterm.m +++ b/src/nsterm.m @@ -1275,7 +1275,13 @@ If a completion candidate is selected (overlay or child frame), @@ -185,7 +200,7 @@ index 8ef344d9fe..5038f9830d 100644 { NSRect windowRect = [view convertRect:r toView:nil]; NSRect screenRect -@@ -7407,6 +7413,112 @@ visual line index for Zoom (skip whitespace-only lines +@@ -7407,6 +7413,117 @@ visual line index for Zoom (skip whitespace-only lines return nil; } @@ -232,6 +247,11 @@ index 8ef344d9fe..5038f9830d 100644 + The data pointer is used only in this loop, before Lisp calls. */ + const unsigned char *data = SDATA (str); + ptrdiff_t byte_len = SBYTES (str); ++ /* 128 lines is a safe upper bound for a completion child frame. ++ The caller rejects buffers larger than 10,000 characters ++ (BUF_ZV(b) - BUF_BEGV(b) > 10000 guard in announceChildFrameCompletion), ++ so the worst case is ~10 KB / 1 byte per line < 128. If a future ++ caller removes that guard, lines beyond 128 are silently skipped; */ + ptrdiff_t line_starts[128]; + ptrdiff_t line_ends[128]; + int nlines = 0; @@ -298,7 +318,7 @@ index 8ef344d9fe..5038f9830d 100644 /* Build accessibility text for window W, skipping invisible text. Populates *OUT_START with the buffer start charpos. Populates *OUT_RUNS with an array of visible runs and *OUT_NRUNS -@@ -7440,9 +7552,13 @@ visual line index for Zoom (skip whitespace-only lines +@@ -7440,9 +7557,13 @@ visual line index for Zoom (skip whitespace-only lines return @""; specpdl_ref count = SPECPDL_INDEX (); @@ -313,7 +333,7 @@ index 8ef344d9fe..5038f9830d 100644 if (b != current_buffer) set_buffer_internal_1 (b); -@@ -8605,6 +8721,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range +@@ -8605,6 +8726,11 @@ - (void)setAccessibilitySelectedTextRange:(NSRange)range [self ensureTextCache]; @@ -325,7 +345,7 @@ index 8ef344d9fe..5038f9830d 100644 specpdl_ref count = SPECPDL_INDEX (); record_unwind_current_buffer (); /* Ensure block_input is always matched by unblock_input even if -@@ -9053,20 +9174,38 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point +@@ -9053,20 +9179,38 @@ - (void)postFocusedCursorNotification:(ptrdiff_t)point && granularity == ns_ax_text_selection_granularity_character); @@ -374,7 +394,7 @@ index 8ef344d9fe..5038f9830d 100644 ns_ax_post_notification_with_info ( self, NSAccessibilitySelectedTextChangedNotification, -@@ -9166,12 +9305,17 @@ user expectation ("w" jumps to next word and reads it). */ +@@ -9166,12 +9310,17 @@ user expectation ("w" jumps to next word and reads it). */ } } @@ -397,7 +417,7 @@ index 8ef344d9fe..5038f9830d 100644 if (cachedText && granularity == ns_ax_text_selection_granularity_line) { -@@ -9236,6 +9380,11 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b +@@ -9236,6 +9385,11 @@ - (void)postCompletionAnnouncementForBuffer:(struct buffer *)b block_input (); specpdl_ref count2 = SPECPDL_INDEX (); @@ -409,7 +429,7 @@ index 8ef344d9fe..5038f9830d 100644 record_unwind_protect_void (unblock_input); record_unwind_current_buffer (); if (b != current_buffer) -@@ -9412,12 +9561,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f +@@ -9412,12 +9566,29 @@ - (void)postAccessibilityNotificationsForFrame:(struct frame *)f if (!b) return; @@ -439,7 +459,7 @@ index 8ef344d9fe..5038f9830d 100644 if (modiff != self.cachedModiff) { self.cachedModiff = modiff; -@@ -9431,6 +9597,7 @@ Text property changes (e.g. face updates from +@@ -9431,6 +9602,7 @@ Text property changes (e.g. face updates from { self.cachedCharsModiff = chars_modiff; [self postTextChangedNotification:point]; @@ -447,7 +467,7 @@ index 8ef344d9fe..5038f9830d 100644 } } -@@ -9453,8 +9620,15 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property +@@ -9453,8 +9625,15 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property displayed in the minibuffer. In normal editing buffers, font-lock and other modes change BUF_OVERLAY_MODIFF on every redisplay, triggering O(overlays) work per keystroke. @@ -465,7 +485,7 @@ index 8ef344d9fe..5038f9830d 100644 goto skip_overlay_scan; int selected_line = -1; -@@ -9500,7 +9674,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property +@@ -9500,7 +9679,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property self.cachedPoint = point; self.cachedMarkActive = markActive; @@ -485,7 +505,7 @@ index 8ef344d9fe..5038f9830d 100644 NSInteger direction = ns_ax_text_selection_direction_discontiguous; if (point > oldPoint) direction = ns_ax_text_selection_direction_next; -@@ -9512,6 +9697,7 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property +@@ -9512,6 +9702,7 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property /* --- Granularity detection --- */ NSInteger granularity = ns_ax_text_selection_granularity_unknown; @@ -493,7 +513,7 @@ index 8ef344d9fe..5038f9830d 100644 [self ensureTextCache]; if (cachedText && oldPoint > 0) { -@@ -9526,7 +9712,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property +@@ -9526,7 +9717,18 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property NSRange newLine = [cachedText lineRangeForRange: NSMakeRange (newIdx, 0)]; if (oldLine.location != newLine.location) @@ -513,7 +533,7 @@ index 8ef344d9fe..5038f9830d 100644 else { NSUInteger dist = (newIdx > oldIdx -@@ -9548,34 +9745,23 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property +@@ -9548,34 +9750,23 @@ frameworks like Vertico bump BOTH BUF_MODIFF (via text property granularity = ns_ax_text_selection_granularity_line; } @@ -561,7 +581,7 @@ index 8ef344d9fe..5038f9830d 100644 { NSWindow *win = [self.emacsView window]; if (win) -@@ -9734,6 +9920,13 @@ - (NSRect)accessibilityFrame +@@ -9734,6 +9925,13 @@ - (NSRect)accessibilityFrame if (vis_start >= vis_end) return @[]; @@ -575,7 +595,7 @@ index 8ef344d9fe..5038f9830d 100644 block_input (); specpdl_ref blk_count = SPECPDL_INDEX (); record_unwind_protect_void (unblock_input); -@@ -10040,6 +10233,10 @@ - (void)dealloc +@@ -10040,6 +10238,10 @@ - (void)dealloc #endif [accessibilityElements release]; @@ -586,7 +606,7 @@ index 8ef344d9fe..5038f9830d 100644 [[self menu] release]; [super dealloc]; } -@@ -11489,6 +11686,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f +@@ -11489,6 +11691,9 @@ - (instancetype) initFrameFromEmacs: (struct frame *)f windowClosing = NO; processingCompose = NO; @@ -596,7 +616,7 @@ index 8ef344d9fe..5038f9830d 100644 scrollbarsNeedingUpdate = 0; fs_state = FULLSCREEN_NONE; fs_before_fs = next_maximized = -1; -@@ -12797,6 +12997,159 @@ - (id)accessibilityFocusedUIElement +@@ -12797,6 +13002,161 @@ - (id)accessibilityFocusedUIElement The existing elements carry cached state (modiff, point) from the previous redisplay cycle. Rebuilding first would create fresh elements with current values, making change detection impossible. */ @@ -697,8 +717,10 @@ index 8ef344d9fe..5038f9830d 100644 + childFrameLastBuffer = BVAR (b, name); + childFrameLastModiff = modiff; + -+ if (!BUFFER_LIVE_P (b)) -+ return; ++ /* BUFFER_LIVE_P(b) is already checked at entry (line above the ++ EQ comparison). No code between that check and here can kill ++ the buffer, so this second check is redundant. */ ++ eassert (BUFFER_LIVE_P (b)); + + /* Skip buffers larger than a typical completion popup. + This avoids scanning eldoc, which-key, or other child @@ -756,7 +778,7 @@ index 8ef344d9fe..5038f9830d 100644 - (void)postAccessibilityUpdates { NSTRACE ("[EmacsView postAccessibilityUpdates]"); -@@ -12807,11 +13160,69 @@ - (void)postAccessibilityUpdates +@@ -12807,11 +13167,69 @@ - (void)postAccessibilityUpdates /* Re-entrance guard: VoiceOver callbacks during notification posting can trigger redisplay, which calls ns_update_end, which calls us