Key changes from previous version:
- Remove overlay text from ns_ax_buffer_text (was causing spurious
'new line' announcements via VoiceOver text diff)
- Do NOT invalidate text cache on overlay change
- Two-reference face detection (handles selected at any position)
- SDATA scan instead of per-char Faref for newline detection
- Zoom tracking via UAZoomChangeFocus for selected candidate row
- Deduplication via cachedCompletionAnnouncement
- Appends overlay before-string/after-string to AX text
- Detects BUF_OVERLAY_MODIFF changes
- Finds highlighted candidate via face text property
- Announces selected candidate via AnnouncementRequested
(fixes 'new line' instead of reading candidate)
- Appends overlay before-string/after-string to AX text
- Detects BUF_OVERLAY_MODIFF changes
- Finds highlighted candidate via face text property
- Announces selected candidate via AnnouncementRequested
(fixes 'new line' instead of reading candidate)
Vertico default renders candidates via overlay before-string which
is invisible to the nsterm.m AX text extraction. vertico-buffer-mode
renders in a real buffer window that the accessibility layer handles.
- Start from today (no past days visible)
- Show 7 days forward
- Skip done/cancelled items
- No duplicate: skip scheduled if deadline already shown
- Suppress early deadline warning if scheduled date exists
- Priority-based sorting within groups (A > B > C)
- Overdue deadlines at top
- Due today / scheduled today separated
- Due soon (upcoming deadlines) in own group
- No duplicates (items match first group only)
- Kyndryl/ZTJ groups by tag without deadline overlap
Split VoiceOver accessibility into 4 logical patches:
0001: Base classes + text extraction (+753)
0002: Buffer/ModeLine/InteractiveSpan implementations (+1716)
0003: EmacsView integration + cursor tracking (+395)
0004: Documentation with known limitations (+75)
Each patch is self-contained: 0001 adds infrastructure that compiles
but doesn't change behavior. 0002 adds protocol implementations.
0003 wires everything into EmacsView. 0004 documents for users.
All patches verified: apply cleanly to current Emacs master,
final state identical to original monolithic patch.
Split the monolithic 3011-line patch into logical pieces:
0001: All new accessibility code (infrastructure, no existing code modified)
0002: EmacsView integration + cursor tracking (wiring only)
0003: Documentation (expanded with known limitations)
Improvements:
- Comprehensive commit messages with testing methodology
- Known limitations documented (text cap, bidi, mode-line icons)
- Documentation expanded with Known Limitations section
- Each patch is self-contained and reviewable
M1: accessibilityRangeForPosition uses specpdl unwind protection for
block_input/unblock_input (consistent with all other methods).
M2: Track BUF_OVERLAY_MODIFF in ensureTextCache — overlay-only changes
(timer-based completion highlight) now invalidate the text cache.
M3: Detect narrowing/widening by comparing cachedTextStart vs BUF_BEGV.
m1: Binary search (O(log n)) for visible runs in both
accessibilityIndexForCharpos and charposForAccessibilityIndex.
m3: Add EmacsAXSpanTypeNone = -1 to enum instead of (EmacsAXSpanType)-1 cast.
m5: Add TODO comment in ns_ax_mode_line_text about non-CHAR_GLYPH limitation.
README: Remove resolved overlay_modiff limitation, document binary search
and narrowing detection, update architecture section.
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.
Add deadlock prevention section to THREADING MODEL, note async
posting in NOTIFICATION STRATEGY, add design decision 6a, and
add deadlock regression test case (#24) to testing checklist.
NSAccessibilityPostNotification may synchronously invoke VoiceOver
callbacks from a background AX server thread. Those callbacks call
dispatch_sync(main_queue) to read buffer state. If the main thread
is still inside the notification-posting method (postAccessibilityUpdates,
windowDidBecomeKey, or postAccessibilityNotificationsForFrame), the
dispatch_sync deadlocks.
Symptom: Emacs hangs on C-x o after M-x list-buffers from Completions
buffer, but only with VoiceOver enabled.
Fix: introduce ns_ax_post_notification() and
ns_ax_post_notification_with_info() wrappers that defer notification
posting via dispatch_async(main_queue). This lets the current method
return and frees the main queue for VoiceOver's dispatch_sync calls.
All 14 notification-posting sites now use the async wrappers.
The previous sed edit added a line but didn't update @@ hunk
headers, causing 'corrupt patch at line 2849'. Fixed:
- Main hunk: 2386 -> 2387 new-side lines
- Subsequent hunks: +1 offset on new-file line numbers
The function definition lacked a return type, causing:
nsterm.m:7149:1: error: a type specifier is required for all declarations
Added 'static BOOL' — the function returns YES/NO and is file-scoped.
Updated patch line counts in header and README accordingly.
B1: Fix memory leak in ns_ax_scan_interactive_spans — [spans copy]
returned +1 retained object never released by caller.
Now returns [[spans copy] autorelease].
B2: Remove dead function ns_ax_utf16_length_for_buffer_range —
defined but never called anywhere in the patch.
B3: Add specpdl unwind protection in
EmacsAccessibilityInteractiveSpan setAccessibilityFocused: —
if Fselect_window signals, block_input is now always matched
by unblock_input via record_unwind_protect_void.
W2: Document ns_ax_event_is_line_nav_key fragility in README
Known Limitations (raw keycodes vs command symbols).
W4: Add comment for #include intervals.h (TEXT_PROP_MEANS_INVISIBLE).
M3: accessibilityBoundsForRange: on EmacsView now delegates to the
focused EmacsAccessibilityBuffer for accurate per-range geometry,
with cursor-rect fallback for Zoom.
M4: Add <NSAccessibility> protocol conformance to
EmacsAccessibilityBuffer @interface declaration.
W1: Expanded commit message listing all new types, functions, DEFSYM
additions, and threading model.
8 methods were missing thread guards: accessibilityNumberOfCharacters,
accessibilitySelectedText, accessibilityStringForRange:,
accessibilityLineForIndex:, accessibilityRangeForLine:,
accessibilityRangeForIndex:, accessibilityVisibleCharacterRange,
setAccessibilitySelectedTextRange:.
All AX getters now consistently dispatch_sync to main thread.
GC safety comment added to InteractiveSpan setAccessibilityFocused:.
- Explicitly set dirvish-hide-details t
- Keep dired-mode-hook as primary trigger
- Add dired-after-readin-hook as failsafe (catches late buffer setup)
- Remove dirvish-directory-view-mode-hook (dirvish handles via its own var)
dirvish-override-dired-mode may bypass dired-mode-hook.
Add hook to dirvish-directory-view-mode-hook as well.
Toggle details with ( in dired/dirvish buffers.
Remove file-time and file-size from default dirvish-attributes so
screen reader reads only filenames. Add D keybinding to toggle
full details on/off in dirvish buffers.
SelectedTextChanged → only for selection changes (mark active)
AnnouncementRequested → only for cursor moves (char/line)
Never both for the same event. Fixes double-speech globally.
- SelectedTextChanged posted only for focused element: prevents completion
buffer from triggering double-speech (old-candidate + new-candidate)
- AnnouncementRequested for char navigation restored (evil block cursor fix):
posted AFTER SelectedTextChanged so VoiceOver cancels its own reading
and uses our explicit char-at-point announcement
- Priority: Medium (was High)
- Remove static Lisp_Object locals; use DEFSYM in syms_of_nsterm (GC-safe)
- Replace Lisp calls in accessibilityIndexForCharpos / charposForAccessibilityIndex
with NSString composed-character traversal (thread-safe, no Lisp needed)
- isAccessibilityFocused reads cachedPoint instead of marker_position off-thread
- Remove double-announcement: character nav uses only SelectedTextChanged
- Line announcement priority: High → Medium (avoid suppressing VO feedback)