patches: isolate C-n/C-p keypath from arrow navigation

This commit is contained in:
2026-02-26 18:41:35 +01:00
parent 059ee5a0ea
commit 7c01587079

View File

@@ -1,13 +1,13 @@
From 442f21965862e63ddd0234777918b999b97c8f9c Mon Sep 17 00:00:00 2001
From 6a256eb9269e0cfc4d1270ef61c228dd9f9989da Mon Sep 17 00:00:00 2001
From: Daneel <daneel@sukany.cz>
Date: Thu, 26 Feb 2026 18:36:53 +0100
Date: Thu, 26 Feb 2026 18:41:28 +0100
Subject: [PATCH] ns: implement AXBoundsForRange and VoiceOver interaction
fixes
---
nsterm.h | 71 ++
nsterm.m | 2168 ++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 2096 insertions(+), 143 deletions(-)
nsterm.m | 2201 ++++++++++++++++++++++++++++++++++++++++++++++++++----
2 files changed, 2133 insertions(+), 139 deletions(-)
diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4c..22828f2 100644
@@ -106,7 +106,7 @@ index 7c1ee4c..22828f2 100644
diff --git a/src/nsterm.m b/src/nsterm.m
index 932d209..b7f0614 100644
index 932d209..da40369 100644
--- a/src/nsterm.m
+++ b/src/nsterm.m
@@ -1104,6 +1104,11 @@ ns_update_end (struct frame *f)
@@ -159,7 +159,7 @@ index 932d209..b7f0614 100644
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -6849,219 +6885,1743 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
@@ -6849,214 +6885,1779 @@ ns_create_font_panel_buttons (id target, SEL select, SEL cancel_action)
/* ==========================================================================
@@ -585,31 +585,43 @@ index 932d209..b7f0614 100644
}
-- (void) noteUserCancelledSelection
+extern Lisp_Object last_command_event;
+
+static bool
+ns_ax_command_is_basic_line_move (void)
+ns_ax_event_is_ctrl_n_or_p (int *which)
{
- font_panel_active = NO;
+ if (!SYMBOLP (real_this_command))
+ return false;
+ Lisp_Object ev = last_command_event;
+ if (CONSP (ev))
+ ev = EVENT_HEAD (ev);
- if (font_panel_result)
- [font_panel_result release];
- font_panel_result = nil;
-
+ if (!FIXNUMP (ev))
+ return false;
- [NSApp stop: self];
+ Lisp_Object next = intern ("next-line");
+ Lisp_Object prev = intern ("previous-line");
+ return EQ (real_this_command, next) || EQ (real_this_command, prev);
+ EMACS_INT c = XFIXNUM (ev);
+ if (c == '\C-n')
+ {
+ if (which)
+ *which = 1;
+ return true;
+ }
+ if (c == '\C-p')
+ {
+ if (which)
+ *which = -1;
+ return true;
+ }
+ return false;
}
-#endif
-- (Lisp_Object) showFontPanel
+static NSString *
+ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
+ struct buffer *b,
+ ptrdiff_t start,
+ ptrdiff_t end,
+ NSString *cachedText)
+static bool
+ns_ax_command_is_basic_line_move (void)
{
- id fm = [NSFontManager sharedFontManager];
- struct font *font = FRAME_OUTPUT_DATA (emacsframe)->font;
@@ -619,14 +631,18 @@ index 932d209..b7f0614 100644
- NSView *buttons;
- BOOL canceled;
-#endif
-
+ if (!SYMBOLP (real_this_command))
+ return false;
-#ifdef NS_IMPL_GNUSTEP
- nsfont = ((struct nsfont_info *) font)->nsfont;
-#else
- nsfont = (NSFont *) macfont_get_nsctfont (font);
-#endif
+ if (!elem || !b || !cachedText || end <= start)
+ return nil;
+ Lisp_Object next = intern ("next-line");
+ Lisp_Object prev = intern ("previous-line");
+ return EQ (real_this_command, next) || EQ (real_this_command, prev);
+}
-#ifdef NS_IMPL_COCOA
- buttons
@@ -636,13 +652,25 @@ index 932d209..b7f0614 100644
- [[fm fontPanel: YES] setAccessoryView: buttons];
- [buttons release];
-#endif
+static NSString *
+ns_ax_completion_text_for_span (EmacsAccessibilityBuffer *elem,
+ struct buffer *b,
+ ptrdiff_t start,
+ ptrdiff_t end,
+ NSString *cachedText)
+{
+ if (!elem || !b || !cachedText || end <= start)
+ return nil;
- [fm setSelectedFont: nsfont isMultiple: NO];
- [fm orderFrontFontPanel: NSApp];
+ NSString *text = nil;
+ struct buffer *oldb = current_buffer;
+ if (b != current_buffer)
+ set_buffer_internal_1 (b);
- [fm setSelectedFont: nsfont isMultiple: NO];
- [fm orderFrontFontPanel: NSApp];
- font_panel_active = YES;
- timeout = make_timespec (0, 100000000);
+ /* Prefer canonical completion candidate string from text property. */
+ ptrdiff_t probes[2] = { start, end - 1 };
+ for (int i = 0; i < 2 && !text; i++)
@@ -655,16 +683,6 @@ index 932d209..b7f0614 100644
+ text = [NSString stringWithLispString:cstr];
+ }
- font_panel_active = YES;
- timeout = make_timespec (0, 100000000);
+ if (!text)
+ {
+ NSUInteger ax_s = [elem accessibilityIndexForCharpos:start];
+ NSUInteger ax_e = [elem accessibilityIndexForCharpos:end];
+ if (ax_e > ax_s && ax_e <= [cachedText length])
+ text = [cachedText substringWithRange:NSMakeRange (ax_s, ax_e - ax_s)];
+ }
- block_input ();
- while (font_panel_active
-#ifdef NS_IMPL_COCOA
@@ -675,11 +693,23 @@ index 932d209..b7f0614 100644
- )
- ns_select_1 (0, NULL, NULL, NULL, &timeout, NULL, YES);
- unblock_input ();
+ if (b != oldb)
+ set_buffer_internal_1 (oldb);
+ if (!text)
+ {
+ NSUInteger ax_s = [elem accessibilityIndexForCharpos:start];
+ NSUInteger ax_e = [elem accessibilityIndexForCharpos:end];
+ if (ax_e > ax_s && ax_e <= [cachedText length])
+ text = [cachedText substringWithRange:NSMakeRange (ax_s, ax_e - ax_s)];
+ }
- if (font_panel_result)
- [font_panel_result autorelease];
+ if (b != oldb)
+ set_buffer_internal_1 (oldb);
-#ifdef NS_IMPL_COCOA
- if (!canceled)
- font_panel_result = nil;
-#endif
+ if (text)
+ {
+ text = [text stringByTrimmingCharactersInSet:
@@ -688,30 +718,26 @@ index 932d209..b7f0614 100644
+ text = nil;
+ }
-#ifdef NS_IMPL_COCOA
- if (!canceled)
- result = font_panel_result;
- font_panel_result = nil;
-#endif
+ return text;
+}
- result = font_panel_result;
- font_panel_result = nil;
- [[fm fontPanel: YES] setIsVisible: NO];
- font_panel_active = NO;
+@implementation EmacsAccessibilityElement
- if (result)
- return ns_font_desc_to_font_spec ([result fontDescriptor],
- result);
+@implementation EmacsAccessibilityElement
- return Qnil;
+- (NSRect)screenRectFromEmacsX:(int)x y:(int)y width:(int)ew height:(int)eh
+{
+ EmacsView *view = self.emacsView;
+ if (!view || ![view window])
+ return NSZeroRect;
- return Qnil;
+
+ NSRect r = NSMakeRect (x, y, ew, eh);
+ NSRect winRect = [view convertRect:r toView:nil];
+ return [[view window] convertRectToScreen:winRect];
@@ -736,25 +762,20 @@ index 932d209..b7f0614 100644
+ return NSAccessibilityUnignoredAncestor (self.emacsView);
}
-- (void)resetCursorRects
+
+- (id)accessibilityWindow
{
-{
- NSRect visible = [self visibleRect];
- NSCursor *currentCursor = FRAME_POINTER_TYPE (emacsframe);
- NSTRACE ("[EmacsView resetCursorRects]");
+ return [self.emacsView window];
+}
-
- if (currentCursor == nil)
- currentCursor = [NSCursor arrowCursor];
+- (id)accessibilityTopLevelUIElement
+{
+ return [self.emacsView window];
+}
- if (!NSIsEmptyRect (visible))
- [self addCursorRect: visible cursor: currentCursor];
+@end
+- (id)accessibilityWindow
+{
+ return [self.emacsView window];
+}
-#if defined (NS_IMPL_GNUSTEP) || MAC_OS_X_VERSION_MIN_REQUIRED < 101300
-#if defined (NS_IMPL_COCOA) && MAC_OS_X_VERSION_MAX_ALLOWED >= 101300
@@ -762,8 +783,17 @@ index 932d209..b7f0614 100644
-#endif
- [currentCursor setOnMouseEntered: YES];
-#endif
-}
+- (id)accessibilityTopLevelUIElement
+{
+ return [self.emacsView window];
}
+@end
-/*****************************************************************************/
-/* Keyboard handling. */
-#define NS_KEYLOG 0
+@implementation EmacsAccessibilityBuffer
+@synthesize cachedText;
+@synthesize cachedTextModiff;
@@ -775,7 +805,7 @@ index 932d209..b7f0614 100644
+@synthesize cachedCompletionOverlayStart;
+@synthesize cachedCompletionOverlayEnd;
+@synthesize cachedCompletionPoint;
+
+- (void)dealloc
+{
+ [cachedText release];
@@ -784,17 +814,11 @@ index 932d209..b7f0614 100644
+ xfree (visibleRuns);
+ [super dealloc];
+}
-/*****************************************************************************/
-/* Keyboard handling. */
-#define NS_KEYLOG 0
+
+/* ---- Text cache ---- */
-- (void)keyDown: (NSEvent *)theEvent
+
+- (void)invalidateTextCache
{
- Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
- int code;
+{
+ [cachedText release];
+ cachedText = nil;
+ if (visibleRuns)
@@ -1417,6 +1441,9 @@ index 932d209..b7f0614 100644
+ else if (point < oldPoint)
+ direction = ns_ax_text_selection_direction_previous;
+
+ int ctrlNP = 0;
+ bool isCtrlNP = ns_ax_event_is_ctrl_n_or_p (&ctrlNP);
+
+ /* Compute granularity from movement distance.
+ Prefer robust line-range comparison for vertical movement,
+ otherwise single char (1) or unknown (0). */
@@ -1447,6 +1474,16 @@ index 932d209..b7f0614 100644
+ }
+ }
+
+ /* Force line semantics for explicit C-n/C-p keystrokes.
+ This isolates the key-path difference from arrow-down/up. */
+ if (isCtrlNP)
+ {
+ direction = (ctrlNP > 0
+ ? ns_ax_text_selection_direction_next
+ : ns_ax_text_selection_direction_previous);
+ granularity = ns_ax_text_selection_granularity_line;
+ }
+
+ NSDictionary *moveInfo = @{
+ @"AXTextStateChangeType": @(ns_ax_text_state_change_selection_move),
+ @"AXTextSelectionDirection": @(direction),
@@ -1464,7 +1501,7 @@ index 932d209..b7f0614 100644
+ line-motion path so VoiceOver tracks the Emacs point reliably. */
+ if ([self isAccessibilityFocused]
+ && cachedText
+ && ns_ax_command_is_basic_line_move ()
+ && (isCtrlNP || ns_ax_command_is_basic_line_move ())
+ && (direction == ns_ax_text_selection_direction_next
+ || direction == ns_ax_text_selection_direction_previous))
+ {
@@ -2038,15 +2075,10 @@ index 932d209..b7f0614 100644
+/*****************************************************************************/
+/* Keyboard handling. */
+#define NS_KEYLOG 0
+
+- (void)keyDown: (NSEvent *)theEvent
+{
+ Mouse_HLInfo *hlinfo = MOUSE_HL_INFO (emacsframe);
+ int code;
unsigned fnKeysym = 0;
static NSMutableArray *nsEvArray;
unsigned int flags = [theEvent modifierFlags];
@@ -8237,6 +9797,28 @@ ns_in_echo_area (void)
- (void)keyDown: (NSEvent *)theEvent
{
@@ -8237,6 +9838,28 @@ ns_in_echo_area (void)
XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop
@@ -2075,7 +2107,7 @@ index 932d209..b7f0614 100644
}
@@ -9474,6 +11056,298 @@ ns_in_echo_area (void)
@@ -9474,6 +11097,298 @@ ns_in_echo_area (void)
return fs_state;
}
@@ -2374,7 +2406,7 @@ index 932d209..b7f0614 100644
@end /* EmacsView */
@@ -9941,6 +11815,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
@@ -9941,6 +11856,14 @@ nswindow_orderedIndex_sort (id w1, id w2, void *c)
return [super accessibilityAttributeValue:attribute];
}