BUF_CHARS_MODIFF fix — the core performance regression:
ensureTextCache checked BUF_MODIFF which font-lock bumps on every
redisplay. Each cursor movement in a large file triggered full buffer
rebuild. Now uses BUF_CHARS_MODIFF (changes only on char insert/delete).
Performance issue: editing large files (>~10KB, >2000 lines) caused
progressive slowdown regardless of VoiceOver status.
Root causes:
1. ns_zoom_find_overlay_candidate_line: called Foverlays_in on the
entire visible buffer range on every redisplay when UAZoomEnabled().
In files with many overlays (font-lock, hl-line, show-paren etc.)
this was O(overlays) Lisp work per keystroke.
2. postAccessibilityNotificationsForFrame: when ns-accessibility-enabled
is non-nil, checked BUF_OVERLAY_MODIFF every redisplay. font-lock
bumps this on every redraw, triggering ns_ax_selected_overlay_text
(another O(overlays) scan) for non-minibuffer windows.
Fix: Both scans now guard with MINI_WINDOW_P check. Overlay completion
frameworks (Vertico, Icomplete, Ivy) only display candidates in
minibuffer windows --- no completion framework puts selected-face
overlays in normal editing buffers. For non-minibuffer windows both
functions return immediately with zero Lisp calls.
Additionally: ns_zoom_find_child_frame_candidate is skipped when
f->child_frame_list is nil (no child frames = no Corfu popup).
Zoom patch 0000 now tracks completion candidates:
- Overlay: Vertico, Icomplete, Ivy (face heuristic on before-string)
- Child frame: Corfu, Company-box (scan buffer text for selected face)
Also fixes duplicate lastCursorRect ivar when applied with VoiceOver.
Zoom (0000) declares lastCursorRect @public in EmacsView.
VoiceOver (0005) was re-declaring it, causing 'duplicate member'
compiler error when both applied together. Removed the duplicate.
VoiceOver patches 0001-0008 now apply cleanly on top of Zoom patch
0000. The full set (git am patches/000*.patch) works without
conflicts. Patch 0005 (integration) merges Zoom fallback and
VoiceOver postAccessibilityUpdates in ns_update_end.
The ivar was declared in patch 0001 but first used in patch 0005,
creating dead code in intermediate commits 0001-0004. Now each
commit only introduces declarations that are immediately used.
Fixes from Opus maintainer review:
1. [BLOCKER] Zoom code completely removed from ALL intermediate patches
(0005-0007 no longer have UAZoom/overlayZoom at any commit point)
2. [BLOCKER] Unified cursor rect ivar: lastCursorRect (was split
between lastZoomCursorRect and lastAccessibilityCursorRect)
3. [HIGH] Child frame static vars moved to EmacsView ivars
(childFrameLastCandidate/Buffer/Modiff — no cross-frame interference)
4. [HIGH] intern_c_string replaced with Qbefore_string/Qafter_string
5. [MEDIUM] Zoom fallback gated by zoomCursorUpdated flag (no double call)
Major changes:
1. Zoom separated into standalone patch 0000
- UAZoomChangeFocus in ns_draw_window_cursor
- Fallback in ns_update_end for window-switch tracking
- No overlayZoomActive (source of split/switch/move bug)
2. VoiceOver patches 0001-0008 are now Zoom-free
- All UAZoom*, overlayZoom*, kUAZoomFocus references removed
- lastAccessibilityCursorRect kept for VoiceOver bounds queries
- Commit messages cleaned of Zoom references
3. README.txt and TESTING.txt rewritten for new structure
Addresses reviewer (Stéphane Marks) feedback:
- Keep Zoom patch separate from VoiceOver work
- Design discussion needed for non-Zoom patches
- Performance: ns-accessibility-enabled=nil for zero overhead
BLOCKER fixes:
1. Remove duplicate ns_ax_face_is_selected, ns_ax_selected_overlay_text,
ns_ax_selected_child_frame_text definitions from patch 0002
(now defined only in 0007/0008 where they belong)
2. Fix idx → point_idx in accessibilityInsertionPointLineNumber (0002)
3. Remove stale 100K cap reference from documentation (0006)
Architecture fix:
- ns_ax_selected_child_frame_text moved from 0007 to 0008
(where it logically belongs)
Verified: all 8 patches apply cleanly on fresh emacs HEAD.
- 0001: remove NS_AX_TEXT_CAP (100K char cap), add lineStartOffsets/
lineCount ivars and method declarations to nsterm.h
- 0002: add lineForAXIndex:/rangeForLine: O(log L) helpers, build line
index in ensureTextCache, replace O(L) line scanning in
accessibilityInsertionPointLineNumber/accessibilityLineForIndex/
accessibilityRangeForLine, free index in invalidateTextCache/dealloc
- 0009 deleted (folded into 0001+0002)
- README.txt: remove NS_AX_TEXT_CAP references, update known
limitations, stress test threshold 50K lines
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.