- 0000: fix double blank line before #ifdef, remove extra block scope braces - 0001: spell out 'AT' -> 'assistive technology'; fix stub comment - 0002: revert unrelated em-dash->triple-dash in windowWillResize strings - 0003-0005: renumber from [PATCH N/9] to [PATCH N/8] - 0006: remove 'Block-style cursors' from Known Limitations; fix @xref note - 0007: remove spurious ns_ax_face_is_selected ChangeLog entry - 0008: refactor goto skip_overlay_scan to nested if; remove spurious blank line - All: renumber series (0000=[PATCH 1/1], 0001-0008=[PATCH 1/8]-[PATCH 8/8])
17 KiB
The review is written to /tmp/emacs-doom-review/REVIEW-FINAL.md (418 lines). Here's the summary of findings:
Review Summary
Overall verdict: READY WITH MINOR FIXES
The series has matured well through six rounds. Architecture is sound, threading model is correct, performance claims are supportable. Two items block upstream submission:
Must fix
Patch 0002 — Contains an unrelated functional change: the window-resize title string changes strstr(t, " — ") and esprintf(..., "%s — ...") from Unicode em-dash to triple-dash ---. This is a behavioral change to non-accessibility code embedded in an accessibility patch. A maintainer will catch it immediately.
Patch 0007 — The commit message claims to introduce ns_ax_face_is_selected as a new function, but no such function appears in the diff. The actual code calls ns_face_name_matches_selected_p from patch 0000. The ChangeLog entry must be corrected.
Structural (must fix before submission)
[PATCH 1/9] numbering — All nine patches are numbered as one series, but the README says 0000 is independent of 0001–0008. For emacs-devel submission, either add a [PATCH 0/9] cover letter explaining the split, or submit 0000 separately as [PATCH 1/1].
Notable nits (fix recommended)
goto skip_overlay_scanin patch 0007 — unusual in Emacs C; Stefan Monnier will likely ask for a nestedifinstead- "Known Limitations" fifth bullet in patch 0006 ("Block-style cursors are handled correctly...") is a feature, not a limitation
- "AT" abbreviation in commit messages — spell out "assistive technology"
- Patch 0006 commit message references an
@xrefthat isn't there
What's clean (no action needed)
Patches 0003, 0004, 0005 are LGTM. The threading model (AX thread → dispatch_sync to main), the @synchronized cache guards, block_input/unblock_input specpdl protection, GNUstep exclusion, and performance claims all check out.
pTTL caching withCFAbsoluteTimeGetCurrent()` is
well motivated; the 1-second TTL justification is documented clearly.
ns_face_name_matches_selected_pusingstrstron face symbol names is an accepted heuristic; the comment acknowledges it.- specpdl unwind protection is correct throughout.
ns_zoom_find_child_frame_candidate: inside the FOR_EACH_FRAME body,block_input()is called beforeSPECPDL_INDEX(). In practice this is safe (SPECPDL_INDEX cannot fail), but the canonical Emacs ordering is SPECPDL_INDEX -> record_unwind -> block_input. Patch 0008 later reverses this order inns_ax_buffer_textwith an explanatory comment; 0000 is left inconsistent. Nit only.- Double blank line before
#ifdef NS_IMPL_COCOAat the top of the new block (~line 93 of the diff). GNU Emacs style uses a single blank line between top-level definitions. - Block scope
{ EmacsView *view = ...; }inns_draw_window_cursoris unnecessary (no outerviewin scope at that point); the extra braces can be removed.
Verdict: LGTM with nits -- fix numbering before submission; the code is otherwise clean.
0001: Add accessibility base classes and helpers [PATCH 2/9]
Subject / commit message Correct format. The commit message abbreviates "AT" for "assistive technology" -- spell it out for an emacs-devel audience.
Code quality
- GC safety analysis on
lispWindowis thorough and correct. @synchronized (self)guards on visibleRuns/lineStartOffsets are correct; they protect against concurrent reads from the AX server thread while the main thread rebuilds the cache.ns_ax_buffer_textcorrectly usesFbuffer_substring_no_propertiesinstead of rawBUF_BYTE_ADDRESSto handle the buffer gap. ✓Fget_char_property (pos, Qinvisible, Qnil)with Qnil meaning the current buffer is correct afterset_buffer_internal_1(b). ✓TEXT_PROP_MEANS_INVISIBLEcorrectly mirrors xdisp.c logic. ✓- The stub
@implementation EmacsAccessibilityBuffer (InteractiveSpans)has a comment reading "full implementation added in patch 0004". Once the series numbering is resolved this should say "in the following patch" or match the final [PATCH N/M] numbers.
Verdict: LGTM with nits.
0002: Implement buffer accessibility element [PATCH 3/9]
SERIOUS ISSUE -- unrelated functional change
Deep in the diff, in the context of windowWillResize:toSize:, the patch
changes:
- char *pos = strstr (t, " \xe2\x80\x94 ");
+ char *pos = strstr (t, " --- ");
...
- esprintf (size_title, "%s \xe2\x80\x94 (%d x %d)", old_title, cols, rows);
+ esprintf (size_title, "%s --- (%d x %d)", old_title, cols, rows);
This replaces a Unicode em-dash (U+2014) with three ASCII hyphens in the functional window-resize title string. This:
- Is unrelated to accessibility.
- Changes the visual appearance of the resize title bar.
- Must be in its own commit (with its own ChangeLog entry and justification) or reverted. A maintainer will reject the patch if they spot this.
All other content in this patch is correct:
@synchronized (self)ininvalidateTextCachecorrectly releases Objective-C objects under the lock;invalidateInteractiveSpansis called outside the lock to avoid mutex inversion. ✓- Binary search in
accessibilityIndexForCharpos:andcharposForAccessibilityIndex:is correct; the O(1) ASCII fast-path is well-motivated and the comment explains why it matters. ✓ setAccessibilitySelectedTextRange:correctly deactivates the mark (with explanatory comment about VoiceOver word-boundary hints). ✓dispatch_asyncfor setters (no return value needed) vs.dispatch_syncfor getters (return value required) is the correct pattern throughout. ✓accessibilityStyleRangeForIndex:is the only getter without its own main-thread check; it relies on two sub-calls that each dispatch individually. Correct but two round-trips instead of one. Minor nit.
Verdict: Needs work -- remove the window title em-dash change.
0003: Add AX notifications and mode-line element [PATCH 4/9]
Subject / commit message Correct. ChangeLog entries are detailed and accurate.
Code quality
postTextChangedNotification:single-character fast path (grab the inserted char forAXTextChangeValue) is a useful optimization.postFocusedCursorNotification:direction:granularity:markActive: oldMarkActive:granularity reasoning -- omit for char moves to prevent block-cursor double-speech -- matches documented WebKit behaviour. ✓postCompletionAnnouncementForBuffer:point:four-level fallback chain (completion--string -> mouse-face -> completions-highlight overlay -> line text) is comprehensive.[ws mutableCopy]/[trims release]in the word-announcement path is correct MRC. ✓EmacsAccessibilityModeLinereturnsNSAccessibilityStaticTextRole;accessibilityValuedelegates tons_ax_mode_line_text. Correct.
Verdict: LGTM.
0004: Add interactive span elements for Tab [PATCH 5/9]
Subject / commit message Correct.
Code quality
ns_ax_scan_interactive_spanspriority ordering (widget > button > follow-link > org-link > completion > keymap-overlay) is a reasonable policy; documented in the function comment.- Skip-to-next-change logic uses the minimum of per-property change points: O(properties * log(buffer)) rather than O(chars). ✓
EmacsAccessibilityInteractiveSpan.isAccessibilityFocusedreadspb.cachedPoint(a plainptrdiff_t) without@synchronized. This is safe becausecachedPointis only written on the main thread and the ivar is machine-word aligned (naturally atomic on ARM64/x86-64). Acceptable; a brief comment noting this would be welcome.setAccessibilityFocused:correctly usesdispatch_asyncwith the Lisp_Object-captured-by-value comment. ✓- Removes the stub
@implementationfrom patch 0001 cleanly. ✓
Verdict: LGTM.
0005: Wire accessibility into EmacsView and redisplay [PATCH 6/9]
Subject / commit message Correct.
Code quality
- Re-entrance guard (
accessibilityUpdating) is placed correctly at the start ofpostAccessibilityUpdates. ✓ ns_update_accessibility_stateusesAXIsProcessTrustedWithOptionswithkAXTrustedCheckOptionPrompt: @NO-- does not prompt the user. ✓(__bridge id)and(__bridge CFDictionaryRef)casts in MRC context are correct for toll-free bridging (no ownership transfer). ✓com.apple.accessibility.apidistributed notification is the standard (if undocumented) mechanism used by WebKit et al. ✓- Window-switch detection via
lastSelectedWindowcomparison is clean. accessibilityBoundsForRange:/accessibilityFrameForRange:/ the legacyaccessibilityAttributeValue:forParameter:delegation chain is complete and handles both modern and pre-10.10 AX API callers. ✓postAccessibilityUpdatescallsrebuildAccessibilityTreebefore the per-element notification loop when the tree is stale, with a clear comment explaining why (cannot diff state after fresh elements are created). ✓
Verdict: LGTM.
0006: doc: Add VoiceOver section to macOS appendix [PATCH 7/9]
Texinfo structure
@node VoiceOver Accessibilityinserted betweenMac / GNUstep EventsandGNUstep Support. Menu entry correctly added. ✓@cindex,@vindex,@itemize @bullet,@subheadingall used correctly. ✓@sectiontitle "VoiceOver Accessibility (macOS)" is appropriate for the macOS appendix.
Content issue
In the "Known Limitations" @itemize, the fifth bullet reads:
Block-style cursors are handled correctly: character navigation announces the character at the cursor position, not the character before it.
This is not a limitation -- it is a correctness feature. It does not belong in a "Known Limitations" section. Remove this bullet or move it to the main feature description above.
Commit message note
The commit message says "Use @xref for cross-reference at sentence start"
but no @xref appears in the added Texinfo. The note is stale from a
previous revision; remove it or add the intended @xref.
Verdict: LGTM with nits -- fix the Known Limitations bullet; clean up the @xref note in the commit message.
0007: Announce overlay completions to VoiceOver [PATCH 8/9]
SERIOUS ISSUE -- commit message describes non-existent function
The commit message states:
* src/nsterm.m (ns_ax_face_is_selected): New static function; matches
'current', 'selected', 'selection' in face symbol names.
No function named ns_ax_face_is_selected appears anywhere in this
patch. The actual code calls ns_face_name_matches_selected_p, which
was introduced in patch 0000. The commit message must be corrected to
remove this spurious ChangeLog entry. A maintainer applying the patch
would immediately notice the claimed function does not appear in the diff.
Code quality
ns_ax_selected_overlay_textusesSDATAfor the newline scan and correctly notes the data pointer is used only before Lisp calls that could trigger GC. ✓- Stack-allocated
line_starts[512]/line_ends[512]is safe given the vertico-count upper bound and the 512-line cap. ✓ BUF_CHARS_MODIFF(notBUF_MODIFF) now gatesValueChanged; the reason (text-property-only changes from font-lock / Vertico prompt bump BUF_MODIFF but not BUF_CHARS_MODIFF) is well-explained. ✓- Separate
ifforBUF_OVERLAY_MODIFF(notelse if) correctly handles frameworks that bump both counters in one command cycle. ✓ - Removing
ensureTextCachefrom the cursor-moved branch prevents the O(visible-buffer-text) rebuild cost on every keystroke. ✓ goto skip_overlay_scanis unusual in Emacs C code. GNU Emacs does use goto for error exits, but using it to skip over a block is more readable as a nestedif (!MINI_WINDOW_P (w) && !didTextChange). Stefan Monnier is likely to request a refactor here.block_input/record_unwind_protect_voidordering inns_ax_buffer_textis corrected and documented. ✓- Em-dash to
---conversions in comments are cosmetic cleanup; acceptable (unlike the functional string changes in patch 0002).
Verdict: Needs work -- fix the commit message; consider refactoring the goto.
0008: Announce child frame completions to VoiceOver [PATCH 9/9]
Subject / commit message Correct and detailed. ChangeLog entries accurately describe all changes.
Code quality
voiceoverSetPointflag design (set insetAccessibilitySelectedText Range:, consumed and reset in the next notification cycle) is correct and clean. ✓singleLineMoveadjacency detection (NSRange boundary comparison) is package-agnostic; no evil/doom/spacemacs-specific code. ✓childFrameLastBufferstored asBVAR(b, name)(an interned symbol, always reachable from obarray) rather than a raw buffer pointer is the correct GC-safe approach. The comment explaining this is clear. ✓childFrameLastCandidateas achar *withxstrdup/xfreeis correct C memory management; freed indealloc. ✓postEchoAreaAnnouncementIfNeededreadsecho_area_buffer[0]directly with a comment explaining whywith_echo_area_bufferis not used. Theminibuf_level == 0guard is correct. ✓- Re-entrance guard comment correctly notes
announceChildFrameCompletionmakes Lisp calls that can trigger redisplay; the guard is placed before the call. ✓ childFrameCompletionActive+ child-frame-still-visible check for restoring VoiceOver focus after popup close is clean. ✓postFocusedCursorNotification:updated to omit direction/granularity for discontiguous jumps (let VoiceOver determine what to read) matches the new reasoning about org-agenda blank-line gaps. ✓- NEWS entry upgraded from
---to+++now that documentation exists (added in patch 0006). ✓ - Minor:
announceChildFrameCompletionhas a spurious blank line between the opening{and the first comment. Cosmetic only.
Verdict: LGTM with nits.
Cross-Cutting Issues
1. Patch numbering inconsistency (structural -- must fix)
All nine patch files carry [PATCH 1/9]-[PATCH 9/9], implying a single series. The README documents 0000 and 0001-0008 as independent. For upstream submission via git-send-email, either:
- Treat all nine as one series with a [PATCH 0/9] cover letter; or
- Separate 0000 into its own submission ([PATCH 1/1]) and renumber the VoiceOver series [PATCH 1/8]-[PATCH 8/8].
2. Em-dash conversion in functional code (serious -- must fix in 0002)
Em-dash to triple-dash conversions in comments (patches 0007, 0008)
are acceptable style cleanup. The same conversion in functional string
literals in patch 0002 (strstr / esprintf window title code) is a
behavioral change that does not belong in an accessibility patch.
3. block_input ordering inconsistency (nit)
Patches 0001-0006 use SPECPDL_INDEX -> record_unwind -> block_input.
Patch 0008 (and a targeted fix in 0007) adopt block_input -> record_unwind
and document why. Both orderings are safe in practice, but the series
leaves them inconsistent across the added functions.
4. "AT" abbreviation in commit messages (nit)
Patches 0001 and 0005 use "AT" for "assistive technology" in commit messages without expanding it. Spell it out on first use.
5. Stub comment reference to "patch 0004" (nit)
The stub @implementation EmacsAccessibilityBuffer (InteractiveSpans) in
patch 0001 says "full implementation added in patch 0004". Once the
series numbering is finalised, update this to match the actual [PATCH N]
number, or change it to "the following patch."
6. Threading model assessment
The dispatch_sync-to-main pattern for all AX getter methods is correct
and matches WebKit's approach. The dispatch_async-for-setters pattern
is correct (no return value needed). The @synchronized blocks on the
shared cache are correctly scoped. The re-entrance guard in
postAccessibilityUpdates is correctly placed. No threading issues
found.
7. GNUstep exclusion
All new code is within #ifdef NS_IMPL_COCOA guards. GNUstep is
correctly excluded. ✓
8. Performance claims
- O(log n) index lookups via binary search on visible runs: correct. ✓
- O(log L) line queries via precomputed lineStartOffsets: correct. ✓
- BUF_MODIFF gating (not BUF_CHARS_MODIFF) for cache validity: correct and thoroughly justified. ✓
- Zero overhead when
ns_accessibility_enabledis nil: verified by TESTING.txt item 14. ✓
Verdict
| Patch | Files | Status |
|---|---|---|
| 0000 | nsterm.h, nsterm.m, NEWS | LGTM with nits -- fix numbering; minor style nits |
| 0001 | nsterm.h, nsterm.m | LGTM with nits -- spell out "AT"; update stub comment |
| 0002 | nsterm.m | Needs work -- remove em-dash/triple-dash change from window-resize string |
| 0003 | nsterm.m | LGTM |
| 0004 | nsterm.m | LGTM |
| 0005 | nsterm.m, NEWS | LGTM |
| 0006 | macos.texi, nsterm.m | LGTM with nits -- fix Known Limitations bullet; clarify @xref note |
| 0007 | nsterm.h, nsterm.m | Needs work -- fix commit message (remove spurious ns_ax_face_is_selected entry); consider refactoring goto |
| 0008 | nsterm.h, nsterm.m, macos.texi, NEWS | LGTM with nits -- remove spurious blank line in method body |
Bottom line: Two patches need targeted fixes before submission (0002: remove unrelated string change; 0007: correct commit message). After those fixes, the series is ready for emacs-devel.