patches: comprehensive review fixes — B1/W1-4/M1-4

B1: setAccessibilityFocused: on EmacsAccessibilityBuffer now checks
    ![NSThread isMainThread] and dispatches to main via dispatch_async.
    Prevents data race + AppKit thread violation from AX server thread.

W1: accessibilityInsertionPointLineNumber and accessibilityLineForIndex:
    now use lineRangeForRange iteration — O(lines) instead of O(chars).

W2: ns_ax_scan_interactive_spans skips non-interactive regions using
    Fnext_single_property_change for each scannable property and
    Fnext_single_char_property_change for keymap overlays.

W3: ns_ax_event_is_line_nav_key inspects Vthis_command against known
    navigation command symbols (next-line, previous-line, evil variants,
    dired variants) instead of raw key codes. Tab/backtab fallback
    retained via last_command_event.

W4: DEFSYM symbols renamed with ns_ax_ prefix (Qns_ax_button, etc.)
    to avoid linker collisions with other Emacs source files.
    Lisp symbol strings unchanged.

M3: Removed dead enum values (CheckBox, TextField, PopUpButton) and
    corresponding dead switch cases.

M4: Improved accessibilityStyleRangeForIndex: comment documenting the
    line-granularity simplification.

README: Updated stats, KNOWN LIMITATIONS, DEFSYM docs, test numbering.
This commit is contained in:
2026-02-27 16:14:47 +01:00
parent a6a3aca678
commit 936c251f11
2 changed files with 163 additions and 103 deletions

View File

@@ -42,8 +42,9 @@ New functions:
ns_ax_frame_for_range: screen rect for a character range via glyph
matrix lookup.
ns_ax_event_is_line_nav_key: detect C-n/C-p/Tab/backtab for
forced line-granularity announcements.
ns_ax_event_is_line_nav_key: detect line navigation commands
via Vthis_command (next-line, previous-line, evil variants)
with Tab/backtab fallback via last_command_event.
ns_ax_scan_interactive_spans: scan visible range for interactive
text properties (widget, button, follow-link, org-link,
@@ -71,9 +72,10 @@ EmacsView extensions:
ns_draw_phys_cursor: stores cursor rect for Zoom, calls
UAZoomChangeFocus with correct CG coordinate-space transform.
DEFSYM additions in syms_of_nsterm: Qwidget, Qbutton, Qfollow_link,
Qorg_link, Qcompletion_list_mode, Qcompletion__string, Qcompletion,
Qcompletions_highlight, Qbacktab.
DEFSYM additions in syms_of_nsterm (ns_ax_ prefix to avoid
collisions): Qns_ax_widget, Qns_ax_button, Qns_ax_follow_link,
Qns_ax_org_link, Qns_ax_completion_list_mode, Qns_ax_completion__string, Qns_ax_completion,
Qns_ax_completions_highlight, Qns_ax_backtab.
Threading model: all Lisp calls on main thread; AX getters use
dispatch_sync to main; index mapping methods are thread-safe (no
@@ -86,7 +88,7 @@ Lisp calls, read only immutable NSString and scalar cache).
etc/NEWS | 11 +
src/nsterm.h | 108 ++
src/nsterm.m | 2870 +++++++++++++++++++++++++++++++++++++++++++++++---
3 files changed, 2841 insertions(+), 148 deletions(-)
3 files changed, 2922 insertions(+), 149 deletions(-)
diff --git a/etc/NEWS b/etc/NEWS
index 7367e3cc..0e4480ad 100644
@@ -114,7 +116,7 @@ diff --git a/src/nsterm.h b/src/nsterm.h
index 7c1ee4cf..542e7d59 100644
--- a/src/nsterm.h
+++ b/src/nsterm.h
@@ -453,6 +453,100 @@ enum ns_return_frame_mode
@@ -453,6 +453,97 @@ enum ns_return_frame_mode
@end
@@ -181,11 +183,8 @@ index 7c1ee4cf..542e7d59 100644
+{
+ EmacsAXSpanTypeButton = 0,
+ EmacsAXSpanTypeLink = 1,
+ EmacsAXSpanTypeCheckBox = 2,
+ EmacsAXSpanTypeTextField = 3,
+ EmacsAXSpanTypePopUpButton = 4,
+ EmacsAXSpanTypeCompletionItem = 5,
+ EmacsAXSpanTypeWidget = 6,
+ EmacsAXSpanTypeCompletionItem = 2,
+ EmacsAXSpanTypeWidget = 3,
+};
+
+/* A lightweight AX element representing one interactive text span
@@ -215,7 +214,7 @@ index 7c1ee4cf..542e7d59 100644
/* ==========================================================================
The main Emacs view
@@ -471,6 +565,13 @@ enum ns_return_frame_mode
@@ -471,6 +562,13 @@ enum ns_return_frame_mode
#ifdef NS_IMPL_COCOA
char *old_title;
BOOL maximizing_resize;
@@ -310,7 +309,7 @@ index 932d209f..ea2de6f2 100644
ns_focus (f, NULL, 0);
NSGraphicsContext *ctx = [NSGraphicsContext currentContext];
@@ -6849,218 +6891,2414 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
@@ -6849,218 +6891,2471 @@ - (BOOL)fulfillService: (NSString *)name withArg: (NSString *)arg
/* ==========================================================================
@@ -661,7 +660,7 @@ index 932d209f..ea2de6f2 100644
-#else
- nsfont = (NSFont *) macfont_get_nsctfont (font);
-#endif
+ Lisp_Object faceSym = Qcompletions_highlight;
+ Lisp_Object faceSym = Qns_ax_completions_highlight;
+ ptrdiff_t begv = BUF_BEGV (b);
+ ptrdiff_t zv = BUF_ZV (b);
+ ptrdiff_t best_start = 0;
@@ -773,51 +772,59 @@ index 932d209f..ea2de6f2 100644
- if (font_panel_result)
- [font_panel_result autorelease];
+/* Detect line-level navigation commands. Inspects Vthis_command
+ (the command symbol being executed) rather than raw key codes so
+ that remapped bindings (e.g., C-j -> next-line) are recognized.
+ Falls back to last_command_event for Tab/backtab which are not
+ bound to a single canonical command symbol. */
+static bool
+ns_ax_event_is_line_nav_key (int *which)
+{
+ Lisp_Object ev = last_command_event;
+ if (CONSP (ev))
+ ev = EVENT_HEAD (ev);
-#ifdef NS_IMPL_COCOA
- if (!canceled)
- font_panel_result = nil;
-#endif
+ if (!FIXNUMP (ev))
+ {
+ /* Handle symbol events: backtab (S-Tab = previous completion). */
+ if (SYMBOLP (ev) && !NILP (ev))
+ {
+ if (EQ (ev, Qbacktab))
+ {
+ if (which)
+ *which = -1;
+ return true;
+ }
+ }
+ return false;
+ }
- result = font_panel_result;
- font_panel_result = nil;
+ EMACS_INT c = XFIXNUM (ev);
+ if (c == 14) /* C-n */
+ /* 1. Check Vthis_command for known navigation command symbols. */
+ if (SYMBOLP (Vthis_command) && !NILP (Vthis_command))
+ {
+ if (which)
+ *which = 1;
+ Lisp_Object cmd = Vthis_command;
+ /* Forward line commands. */
+ if (EQ (cmd, intern_c_string ("next-line"))
+ || EQ (cmd, intern_c_string ("dired-next-line"))
+ || EQ (cmd, intern_c_string ("evil-next-line"))
+ || EQ (cmd, intern_c_string ("evil-next-visual-line")))
+ {
+ if (which) *which = 1;
+ return true;
+ }
+ /* Backward line commands. */
+ if (EQ (cmd, intern_c_string ("previous-line"))
+ || EQ (cmd, intern_c_string ("dired-previous-line"))
+ || EQ (cmd, intern_c_string ("evil-previous-line"))
+ || EQ (cmd, intern_c_string ("evil-previous-visual-line")))
+ {
+ if (which) *which = -1;
+ return true;
+ }
+ }
+
+ /* 2. Fallback: check raw key events for Tab/backtab. */
+ Lisp_Object ev = last_command_event;
+ if (CONSP (ev))
+ ev = EVENT_HEAD (ev);
+
+ if (SYMBOLP (ev) && EQ (ev, Qns_ax_backtab))
+ {
+ if (which) *which = -1;
+ return true;
+ }
+ if (c == 16) /* C-p */
+ if (FIXNUMP (ev) && XFIXNUM (ev) == 9) /* Tab */
+ {
+ if (which)
+ *which = -1;
+ return true;
+ }
+ if (c == 9) /* Tab — next completion/link */
+ {
+ if (which)
+ *which = 1;
+ if (which) *which = 1;
+ return true;
+ }
+ return false;
@@ -905,7 +912,7 @@ index 932d209f..ea2de6f2 100644
+ns_ax_get_span_label (ptrdiff_t start, ptrdiff_t end,
+ Lisp_Object buf_obj)
+{
+ Lisp_Object cs = ns_ax_text_prop_at (start, Qcompletion__string,
+ Lisp_Object cs = ns_ax_text_prop_at (start, Qns_ax_completion__string,
+ buf_obj);
+ if (STRINGP (cs))
+ return [NSString stringWithLispString: cs];
@@ -1001,7 +1008,7 @@ index 932d209f..ea2de6f2 100644
+ /* Symbols are interned once at startup via DEFSYM in syms_of_nsterm;
+ reference them directly here (GC-safe, no repeated obarray lookup). */
+
+ BOOL is_completion_buf = EQ (BVAR (b, major_mode), Qcompletion_list_mode);
+ BOOL is_completion_buf = EQ (BVAR (b, major_mode), Qns_ax_completion_list_mode);
+
+ NSMutableArray *spans = [NSMutableArray array];
+ ptrdiff_t pos = vis_start;
@@ -1012,25 +1019,25 @@ index 932d209f..ea2de6f2 100644
+ EmacsAXSpanType span_type = (EmacsAXSpanType) -1;
+ Lisp_Object limit_prop = Qnil;
+
+ if (!NILP (Fplist_get (plist, Qwidget, Qnil)))
+ if (!NILP (Fplist_get (plist, Qns_ax_widget, Qnil)))
+ {
+ span_type = EmacsAXSpanTypeWidget;
+ limit_prop = Qwidget;
+ limit_prop = Qns_ax_widget;
+ }
+ else if (!NILP (Fplist_get (plist, Qbutton, Qnil)))
+ else if (!NILP (Fplist_get (plist, Qns_ax_button, Qnil)))
+ {
+ span_type = EmacsAXSpanTypeButton;
+ limit_prop = Qbutton;
+ limit_prop = Qns_ax_button;
+ }
+ else if (!NILP (Fplist_get (plist, Qfollow_link, Qnil)))
+ else if (!NILP (Fplist_get (plist, Qns_ax_follow_link, Qnil)))
+ {
+ span_type = EmacsAXSpanTypeLink;
+ limit_prop = Qfollow_link;
+ limit_prop = Qns_ax_follow_link;
+ }
+ else if (!NILP (Fplist_get (plist, Qorg_link, Qnil)))
+ else if (!NILP (Fplist_get (plist, Qns_ax_org_link, Qnil)))
+ {
+ span_type = EmacsAXSpanTypeLink;
+ limit_prop = Qorg_link;
+ limit_prop = Qns_ax_org_link;
+ }
+ else if (is_completion_buf
+ && !NILP (Fplist_get (plist, Qmouse_face, Qnil)))
@@ -1039,7 +1046,7 @@ index 932d209f..ea2de6f2 100644
+ don't accidentally merge two column-adjacent candidates
+ whose mouse-face regions may share padding whitespace.
+ Fall back to mouse-face if completion--string is absent. */
+ Lisp_Object cs_sym = Qcompletion__string;
+ Lisp_Object cs_sym = Qns_ax_completion__string;
+ Lisp_Object cs_val = ns_ax_text_prop_at (pos, cs_sym, buf_obj);
+ span_type = EmacsAXSpanTypeCompletionItem;
+ limit_prop = NILP (cs_val) ? Qmouse_face : cs_sym;
@@ -1063,7 +1070,34 @@ index 932d209f..ea2de6f2 100644
+
+ if ((NSInteger) span_type == -1)
+ {
+ pos++;
+ /* Skip to the next position where any interactive property
+ changes. Try each scannable property in turn and take
+ the nearest change point — O(properties) per gap rather
+ than O(chars). Fall back to pos+1 as safety net. */
+ ptrdiff_t next_interesting = vis_end;
+ Lisp_Object skip_props[5]
+ = { Qns_ax_widget, Qns_ax_button, Qns_ax_follow_link,
+ Qns_ax_org_link, Qmouse_face };
+ for (int sp = 0; sp < 5; sp++)
+ {
+ ptrdiff_t np
+ = ns_ax_next_prop_change (pos, skip_props[sp],
+ buf_obj, vis_end);
+ if (np > pos && np < next_interesting)
+ next_interesting = np;
+ }
+ /* Also check overlay keymap changes. */
+ Lisp_Object np_ov
+ = Fnext_single_char_property_change (make_fixnum (pos),
+ Qkeymap, buf_obj,
+ make_fixnum (vis_end));
+ if (FIXNUMP (np_ov))
+ {
+ ptrdiff_t npv = XFIXNUM (np_ov);
+ if (npv > pos && npv < next_interesting)
+ next_interesting = npv;
+ }
+ pos = (next_interesting > pos) ? next_interesting : pos + 1;
+ continue;
+ }
+
@@ -1101,11 +1135,8 @@ index 932d209f..ea2de6f2 100644
+{
+ switch (self.spanType)
+ {
+ case EmacsAXSpanTypeLink: return NSAccessibilityLinkRole;
+ case EmacsAXSpanTypeCheckBox: return NSAccessibilityCheckBoxRole;
+ case EmacsAXSpanTypeTextField: return NSAccessibilityTextFieldRole;
+ case EmacsAXSpanTypePopUpButton: return NSAccessibilityPopUpButtonRole;
+ default: return NSAccessibilityButtonRole;
+ case EmacsAXSpanTypeLink: return NSAccessibilityLinkRole;
+ default: return NSAccessibilityButtonRole;
+ }
+}
+
@@ -1231,7 +1262,7 @@ index 932d209f..ea2de6f2 100644
+ {
+ ptrdiff_t p = probes[i];
+ Lisp_Object cstr = Fget_char_property (make_fixnum (p),
+ Qcompletion__string,
+ Qns_ax_completion__string,
+ Qnil);
+ if (STRINGP (cstr))
+ text = [NSString stringWithLispString:cstr];
@@ -1239,7 +1270,7 @@ index 932d209f..ea2de6f2 100644
+ {
+ /* Fallback: 'completion property used by display-completion-list. */
+ cstr = Fget_char_property (make_fixnum (p),
+ Qcompletion,
+ Qns_ax_completion,
+ Qnil);
+ if (STRINGP (cstr))
+ text = [NSString stringWithLispString:cstr];
@@ -1760,6 +1791,16 @@ index 932d209f..ea2de6f2 100644
+ if (!flag)
+ return;
+
+ /* VoiceOver may call this from the AX server thread.
+ All Lisp reads, block_input, and AppKit calls require main. */
+ if (![NSThread isMainThread])
+ {
+ dispatch_async (dispatch_get_main_queue (), ^{
+ [self setAccessibilityFocused:flag];
+ });
+ return;
+ }
+
+ struct window *w = [self validWindow];
+ if (!w || !WINDOW_LEAF_P (w))
+ return;
@@ -1816,12 +1857,19 @@ index 932d209f..ea2de6f2 100644
+ if (point_idx > [cachedText length])
+ point_idx = [cachedText length];
+
+ /* Count newlines from start to point_idx. */
+ /* Count lines by iterating lineRangeForRange from the start.
+ Each call jumps an entire line — O(lines) not O(chars). */
+ NSInteger line = 0;
+ for (NSUInteger i = 0; i < point_idx; i++)
+ NSUInteger scan = 0;
+ NSUInteger len = [cachedText length];
+ while (scan < point_idx && scan < len)
+ {
+ if ([cachedText characterAtIndex:i] == '\n')
+ line++;
+ NSRange lr = [cachedText lineRangeForRange:NSMakeRange (scan, 0)];
+ NSUInteger next = NSMaxRange (lr);
+ if (next <= scan) break; /* safety */
+ if (next > point_idx) break;
+ line++;
+ scan = next;
+ }
+ return line;
+}
@@ -1866,12 +1914,18 @@ index 932d209f..ea2de6f2 100644
+ if (idx > [cachedText length])
+ idx = [cachedText length];
+
+ /* Count newlines from start of cachedText to idx. */
+ /* Count lines by iterating lineRangeForRange — O(lines). */
+ NSInteger line = 0;
+ for (NSUInteger i = 0; i < idx; i++)
+ NSUInteger scan = 0;
+ NSUInteger len = [cachedText length];
+ while (scan < idx && scan < len)
+ {
+ if ([cachedText characterAtIndex:i] == '\n')
+ line++;
+ NSRange lr = [cachedText lineRangeForRange:NSMakeRange (scan, 0)];
+ NSUInteger next = NSMaxRange (lr);
+ if (next <= scan) break;
+ if (next > idx) break;
+ line++;
+ scan = next;
+ }
+ return line;
+}
@@ -1938,7 +1992,9 @@ index 932d209f..ea2de6f2 100644
+
+- (NSRange)accessibilityStyleRangeForIndex:(NSInteger)index
+{
+ /* Return the range of the current line — simple approach. */
+ /* Return the range of the current line. A more accurate
+ implementation would return face/font property boundaries,
+ but line granularity is acceptable for VoiceOver. */
+ NSInteger line = [self accessibilityLineForIndex:index];
+ return [self accessibilityRangeForLine:line];
+}
@@ -2325,7 +2381,7 @@ index 932d209f..ea2de6f2 100644
+ /* 1. completion--string at point. */
+ Lisp_Object cstr
+ = Fget_char_property (make_fixnum (point),
+ Qcompletion__string, Qnil);
+ Qns_ax_completion__string, Qnil);
+ announceText = ns_ax_completion_string_from_prop (cstr);
+
+ /* 2. Fallback: full line text. */
@@ -2393,7 +2449,7 @@ index 932d209f..ea2de6f2 100644
+ or a list ("candidate" "annotation") for annotated completions.
+ In the list case, use car (the completion itself). */
+ Lisp_Object cstr = Fget_char_property (make_fixnum (point),
+ Qcompletion__string,
+ Qns_ax_completion__string,
+ Qnil);
+ announceText = ns_ax_completion_string_from_prop (cstr);
+
@@ -2440,7 +2496,7 @@ index 932d209f..ea2de6f2 100644
+ /* 3) Fallback: check completions-highlight overlay span at point. */
+ if (!announceText)
+ {
+ Lisp_Object faceSym = Qcompletions_highlight;
+ Lisp_Object faceSym = Qns_ax_completions_highlight;
+ Lisp_Object overlays = Foverlays_at (make_fixnum (point), Qnil);
+ Lisp_Object tail;
+ for (tail = overlays; CONSP (tail); tail = XCDR (tail))
@@ -2873,7 +2929,7 @@ index 932d209f..ea2de6f2 100644
int code;
unsigned fnKeysym = 0;
static NSMutableArray *nsEvArray;
@@ -8237,6 +10475,31 @@ - (void)windowDidBecomeKey /* for direct calls */
@@ -8237,6 +10532,31 @@ - (void)windowDidBecomeKey /* for direct calls */
XSETFRAME (event.frame_or_window, emacsframe);
kbd_buffer_store_event (&event);
ns_send_appdefined (-1); // Kick main loop
@@ -3238,20 +3294,20 @@ index 932d209f..ea2de6f2 100644
@end /* EmacsView */
@@ -11303,6 +13892,18 @@ Convert an X font name (XLFD) to an NS font name.
@@ -11303,7 +13892,18 @@ Convert an X font name (XLFD) to an NS font name.
DEFSYM (Qns_drag_operation_generic, "ns-drag-operation-generic");
DEFSYM (Qns_handle_drag_motion, "ns-handle-drag-motion");
+ /* Accessibility span scanning symbols. */
+ DEFSYM (Qwidget, "widget");
+ DEFSYM (Qbutton, "button");
+ DEFSYM (Qfollow_link, "follow-link");
+ DEFSYM (Qorg_link, "org-link");
+ DEFSYM (Qcompletion_list_mode, "completion-list-mode");
+ DEFSYM (Qcompletion__string, "completion--string");
+ DEFSYM (Qcompletion, "completion");
+ DEFSYM (Qcompletions_highlight, "completions-highlight");
+ DEFSYM (Qbacktab, "backtab");
+ DEFSYM (Qns_ax_widget, "widget");
+ DEFSYM (Qns_ax_button, "button");
+ DEFSYM (Qns_ax_follow_link, "follow-link");
+ DEFSYM (Qns_ax_org_link, "org-link");
+ DEFSYM (Qns_ax_completion_list_mode, "completion-list-mode");
+ DEFSYM (Qns_ax_completion__string, "completion--string");
+ DEFSYM (Qns_ax_completion, "completion");
+ DEFSYM (Qns_ax_completions_highlight, "completions-highlight");
+ DEFSYM (Qns_ax_backtab, "backtab");
+ /* Qmouse_face and Qkeymap are defined in textprop.c / keymap.c. */
+
Fput (Qalt, Qmodifier_value, make_fixnum (alt_modifier));

View File

@@ -3,7 +3,7 @@ EMACS NS VOICEOVER ACCESSIBILITY PATCH
patch: 0001-ns-implement-AXBoundsForRange-for-macOS-Zoom-cursor-.patch
author: Martin Sukany <martin@sukany.cz>
files: src/nsterm.h (+108 lines)
src/nsterm.m (+2588 ins, -140 del, +2448 net)
src/nsterm.m (+2638 ins, -140 del, +2498 net)
OVERVIEW
@@ -36,8 +36,6 @@ OVERVIEW
also covers completion announcements for the *Completions* buffer and
Tab-navigable interactive spans for buttons, links, checkboxes,
Org-mode links, completion candidates, and keymap overlays.
(EmacsAXSpanTypeCheckBox is reserved for future use but not
currently scanned.)
ARCHITECTURE
@@ -367,10 +365,10 @@ INTERACTIVE SPANS
column-adjacent completion candidates from being merged into one span
when their mouse-face regions share padding whitespace.
All property symbols (Qwidget, Qbutton, Qfollow_link, Qorg_link,
Qcompletion__string, Qcompletion, Qcompletions_highlight, Qbacktab,
Qcompletion_list_mode) are registered with DEFSYM in syms_of_nsterm
and referenced directly -- no repeated intern() calls.
All property symbols are registered with DEFSYM in syms_of_nsterm
using ns_ax_ prefixed C variable names (e.g., Qns_ax_button for
"button") to avoid collisions with other Emacs source files.
Referenced directly -- no repeated intern() calls.
Each span is allocated, configured, added to the spans array, then
released (the array retains it). The function returns an autoreleased
@@ -505,8 +503,8 @@ KNOWN LIMITATIONS
covers the common case, but overlay-only changes with a stationary
point would be missed. A future fix would compare overlay_modiff.
- Interactive span scan is O(n) in the visible buffer range. Every
character position is visited to find property boundaries. For
- Interactive span scan uses property-change jumps to skip
non-interactive regions, but still visits every property boundary. For
large visible buffers this scan runs on every redisplay cycle
whenever interactiveSpansDirty is set. An optimization would use
next_single_property_change to skip non-interactive regions in bulk.
@@ -516,6 +514,12 @@ KNOWN LIMITATIONS
Mode lines with icon fonts (e.g. doom-modeline with nerd-font)
produce incomplete or garbled accessibility text.
- Line counting (accessibilityInsertionPointLineNumber,
accessibilityLineForIndex:) uses O(lines) iteration via
lineRangeForRange. For buffers with tens of thousands of visible
lines this is acceptable but not optimal. A line-number cache
keyed on cachedTextModiff could reduce this to O(1).
- Buffers larger than NS_AX_TEXT_CAP (100,000 UTF-16 units) are
truncated. The truncation is silent; AT tools navigating past the
truncation boundary may behave unexpectedly.
@@ -527,11 +531,11 @@ KNOWN LIMITATIONS
has a different accessibility model and requires separate work.
- Line navigation detection (ns_ax_event_is_line_nav_key) checks
raw key codes (C-n = 14, C-p = 16, Tab = 9, backtab symbol).
Users who remap keys to navigation commands (e.g. C-j -> next-line)
will not get forced line-granularity announcements for those
bindings. A future improvement would inspect Vthis_command
against known navigation command symbols instead.
Vthis_command against known navigation command symbols
(next-line, previous-line, evil-next-line, etc.) and falls back
to raw key codes for Tab/backtab. Custom navigation commands
not in the recognized list will not get forced line-granularity
announcements.
- UAZoomChangeFocus always uses kUAZoomFocusTypeInsertionPoint
regardless of cursor style (box, bar, hbar). This is cosmetically
@@ -600,9 +604,9 @@ TESTING CHECKLIST
C-x o to switch windows. Emacs must not hang.
Stress test:
26. Open a large file (>5000 lines). Navigate with C-v / M-v.
25. Open a large file (>5000 lines). Navigate with C-v / M-v.
Verify no significant lag in VoiceOver speech response.
27. Open an org-mode file with many folded sections. Verify that
26. Open an org-mode file with many folded sections. Verify that
folded (invisible) text is not announced during navigation.
-- end of README --