Skip to content

Port four low-risk fixes from upstream PR #1379#528

Merged
schuyler merged 8 commits into
mainfrom
claude/pr1379-tier1-fixes-582f739b
Jul 13, 2026
Merged

Port four low-risk fixes from upstream PR #1379#528
schuyler merged 8 commits into
mainfrom
claude/pr1379-tier1-fixes-582f739b

Conversation

@schuyler

Copy link
Copy Markdown
Owner

Ports four low-risk fixes triaged from upstream MacDownApp/macdown#1379 — Roberto Bissanti's Sparkle-modernization branch (GitHub @wltb). Each fix was reviewed cold independently before landing; the full suite is green (1074 tests, 0 unexpected).

Fixes

Commit Upstream What
6494c90 05d5153 Fix EXC_BAD_ACCESS crashMPDocument.editor IBOutlet was unsafe_unretained; now weak.
806f6ef 2b09e4e Deprecated -insertText:-insertText:replacementRange: at 6 call sites, with 6 new tests.
20ba916 9e62b44 + c807ff7 Modernize MPHomebrewSubprocessController (NSTask.terminationHandler, main-queue delivery, retain-cycle break) and fix a real bug — see note below.
4300766 604664b Remove stale xib outlets (location, supportText) that logged a "Failed to connect outlet" warning on every nib load.

Fix 3 is scope-upgraded (with approval)

Reviewing the Homebrew modernization surfaced a latent bug: launchPath = @"brew" is a bare relative path, and NSTask never searches PATH, so -launch threw NSInvalidArgumentException every time — the Terminal preferences pane's Homebrew detection has never actually worked (it always reported "not installed"). Beyond the faithful port, this now resolves Homebrew's absolute path (/opt/homebrew/bin/brew, then /usr/local/bin/brew) and adopts the non-deprecated executableURL + -launchAndReturnError:. All three completion paths (not-found, launch-failure, success) deliver the handler on the main queue, since the caller mutates AppKit UI directly.

This is a user-visible behavior change: the Terminal preferences pane will now correctly detect an installed Homebrew.

Testing

  • Full suite: 1074 tests, 0 unexpected failures (the 76 failures are the codebase's pre-existing XCTExpectFailure cases).
  • New tests: 6 for the -insertText:replacementRange: migration; 3 for the Homebrew controller (success delivers output on the main thread; not-found delivers nil on the main thread even when invoked off-main; the terminationHandler retain-cycle break holds) — the latter use a test-only seam stubbing brew resolution with /bin/echo for determinism without a real Homebrew install.

Suggested follow-up (not in this PR)

Two test-only categories still redeclare editor as unsafe_unretained (MacDownTests/MPDocumentLifecycleTests.m:27, MacDownTests/MPScrollSyncTests.m:29), stale since the weak-outlet fix. Confirmed harmless (ARC uses the primary class's weak synthesis and the tests hold strong locals) but misleading — worth a one-line cleanup.

Ports by @wltb (Roberto Bissanti).

Related to #1379
Related to #526

schuyler added 5 commits July 11, 2026 02:36
…ained

'editor' was the only IBOutlet on the class declared unsafe_unretained;
every sibling outlet (toolbar, splitView, editorContainer, preview, ...)
is weak. unsafe_unretained is a raw, non-zeroing pointer: if the
MPEditorView is ever deallocated and recreated (window/layout churn),
'editor' keeps pointing at freed memory, and the next message sent to
it is a dangling-pointer access, i.e. EXC_BAD_ACCESS. weak makes the
pointer nil itself out instead, turning a crash into a safe no-op.

Ported from MacDownApp/macdown@05d5153 by Roberto Bissanti.

Related to #1379.
The 1-arg -insertText: has been deprecated since macOS 10.11 in favor of
the 2-arg -insertText:replacementRange:. Six call sites still used the old
form (five in NSTextView+Autocomplete.m, one in MPDocument's
-insertNewParagraph:), the last stragglers in a file that otherwise already
uses the 2-arg API. Each now passes NSMakeRange(NSNotFound, 0), Apple's
documented sentinel for "use the current selection, or the marked (IME
composition) range if there is one" -- preserving the exact behavior the
deprecated call had.

Adds MPInsertTextReplacementRangeTests with six tests, one per converted
site. On the current SDK (macOS 15.6.1 / Xcode 16.1) the deprecated 1-arg
form funnels through our 2-arg override but passes the live selection as the
replacement range rather than NSNotFound; the tests discriminate on that,
failing before the fix and passing after.

Ports MacDownApp/macdown@2b09e4e by Roberto Bissanti.

Related to #1379
The `location` and `supportText` outlets in the Terminal preferences xib
are leftovers from a code-side rename to `locationTextField`/
`supportTextField` that was never mirrored in the nib. Both pointed at the
same destination views as the correctly-named outlets that are still wired,
so they were harmless -- but each logged a "Failed to connect outlet ...
missing setter or instance variable" warning on every nib load. Removing
them silences that noise.

Ports MacDownApp/macdown@604664b by Roberto Bissanti.

Related to #1379
Two things, both rooted in the Terminal preferences pane's Homebrew check.

First, the modernization ported from upstream: the controller used the
pre-GCD -readToEndOfFileInBackgroundAndNotify + NSNotificationCenter pattern,
whose background read is scheduled at init on a QoS the app doesn't control --
the kind of priority-inversion Xcode's Thread Performance Checker flags as a
Hang Risk when a Preferences pane switch happens mid-flight. Rewrote it around
NSTask.terminationHandler, dispatching completion back to the main queue (the
caller mutates AppKit UI directly), and broke the resulting terminationHandler
retain cycle by niling it as the block's first statement under a scoped
-Warc-retain-cycles pragma.

Second, a latent bug the modernization surfaced: launchPath was the bare
relative string "brew". NSTask never consults PATH, so -launch threw
NSInvalidArgumentException every time and the caller silently fell into its
"Homebrew not installed" path -- detection has never actually worked. Resolve
an absolute path (/opt/homebrew/bin/brew, then /usr/local/bin/brew) before
launching, and adopt the non-deprecated executableURL + -launchAndReturnError:
instead of the deprecated -launch/@try-catch. All three outcomes (not found,
launch failure, success) now deliver the handler on the main queue.

Adds MPHomebrewSubprocessControllerTests (success delivers output on the main
thread, not-found delivers nil on the main thread even when invoked from a
background queue, and the terminationHandler cycle-break holds) via a small
test-only seam that overrides brew-path resolution with /bin/echo so the tests
are deterministic without a real Homebrew install.

Ports MacDownApp/macdown@9e62b44 and @c807ff7 by Roberto Bissanti; the
launchPath fix is additional.

Related to #526
Related to #1379
Documents the four fixes ported from upstream MacDownApp/macdown PR #1379:
the weak-outlet crash fix, the -insertText:replacementRange: migration, the
Homebrew-detection fix, and the stale Terminal-preferences outlet cleanup.

Related to #1379
@github-actions

github-actions Bot commented Jul 11, 2026

Copy link
Copy Markdown
Contributor

Code Coverage Report

Current Coverage: 0.00%

Coverage Details (Summary)
Coverage unavailable - xccov failed

schuyler added 3 commits July 11, 2026 12:29
Two test-only categories still redeclared `editor` as `unsafe_unretained`,
left over from before the primary `MPDocument.editor` property became `weak`.
Harmless at runtime (ARC synthesizes accessors from the primary declaration,
and the tests hold their own strong references to the editor), but the stale
attribute misrepresents the property's actual ownership. Match `(weak)`.

Related to #1379
@schuyler
schuyler merged commit f74b9bb into main Jul 13, 2026
10 of 12 checks passed
@schuyler
schuyler deleted the claude/pr1379-tier1-fixes-582f739b branch July 13, 2026 19:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant