Skip to content

Persist user's spell/grammar toggle across caret movement#36

Merged
luca-chen198 merged 1 commit into
mainfrom
34-spell-check-toggle-doesnt-persist-coordinator-resets-it-on-caret-movement
May 19, 2026
Merged

Persist user's spell/grammar toggle across caret movement#36
luca-chen198 merged 1 commit into
mainfrom
34-spell-check-toggle-doesnt-persist-coordinator-resets-it-on-caret-movement

Conversation

@Nicolas-Py
Copy link
Copy Markdown
Collaborator

Closes #34.

Summary

  • updateAutocorrectSettings no longer overwrites the user's manual "off" when the caret leaves a code/LaTeX/link suppress zone. The restore branch reads a per-coordinator preference instead of hardcoded true.
  • New SpellCheckingPolicy on MarkdownEditorConfiguration lets embedders seed initial state. New onSpellCheckingPolicyChanged callback on NativeTextViewWrapper lets them persist user toggles (e.g. to UserDefaults) and feed the value back on next launch.
  • NativeTextView overrides toggleContinuousSpellChecking(_:), toggleGrammarChecking(_:), and toggleAutomaticSpellingCorrection(_:) to capture context-menu toggles into the coordinator state.

Defaults match prior behavior — SpellCheckingPolicy.default is all-on, so existing callers see no change.

Files

  • Configuration/MarkdownEditorConfiguration.swiftSpellCheckingPolicy struct + spellChecking field.
  • TextView/Coordinator/NativeTextViewCoordinator.swiftuserPrefers* mirror, onSpellCheckingPolicyChanged callback, didToggleSpellCheckingPolicy(textView:) handler.
  • TextView/Coordinator/NativeTextViewCoordinator+Autocorrect.swift — restore branch honors userPrefers*. Suppress-zone path unchanged.
  • TextView/NativeTextView/NativeTextView+SpellingToggles.swift — new file, AppKit toggle action overrides.
  • TextView/NativeTextViewWrapper.swiftmakeNSView reads from configuration.spellChecking; new init param threads the callback through makeCoordinator.

Embedder usage

var config = MarkdownEditorConfiguration.default
config.spellChecking = SpellCheckingPolicy(
    continuousSpellChecking: UserDefaults.standard.object(forKey: "spell") as? Bool ?? true,
    grammarChecking: UserDefaults.standard.object(forKey: "grammar") as? Bool ?? true,
    automaticSpellingCorrection: UserDefaults.standard.object(forKey: "autocorrect") as? Bool ?? true
)

NativeTextViewWrapper(
    text: $text,
    configuration: config,
    onSpellCheckingPolicyChanged: { policy in
        UserDefaults.standard.set(policy.continuousSpellChecking, forKey: "spell")
        UserDefaults.standard.set(policy.grammarChecking, forKey: "grammar")
        UserDefaults.standard.set(policy.automaticSpellingCorrection, forKey: "autocorrect")
    }
)

Test plan

  • Open a doc with a code block. Disable "Check Spelling While Typing" via context menu. Type across the code-block boundary and confirm the underline does not return.
  • Same flow for "Check Grammar With Spelling" and "Correct Spelling Automatically".
  • With an embedder wired to UserDefaults: relaunch the app and confirm the previous off-state survives.
  • Default config (no spellChecking override) behaves identically to main — all three toggles on at launch.
  • swift build and swift test clean.

Out of scope

Smart Quotes (isAutomaticQuoteSubstitutionEnabled) has the same caret-movement bug pattern. Tracked separately.

Closes #34.

`updateAutocorrectSettings` ran on every selection change and
unconditionally restored the three spell-related flags to `true` when
the caret left a code/LaTeX/link suppress zone, overwriting a manual
"off" set via the context menu. `makeNSView` also hardcoded the initial
state to `true` with no way for embedders to seed it or observe changes.

- Add `SpellCheckingPolicy` to `MarkdownEditorConfiguration` so the
  initial state of the three toggles can be configured.
- Track the user's preference on `NativeTextViewCoordinator` and update
  it from new `toggleContinuousSpellChecking` / `toggleGrammarChecking`
  / `toggleAutomaticSpellingCorrection` overrides on `NativeTextView`.
- In the restore branch of `updateAutocorrectSettings`, use the tracked
  preference instead of hardcoded `true`. Suppress-zone path unchanged.
- Expose `onSpellCheckingPolicyChanged` on `NativeTextViewWrapper` so
  embedders can persist the policy and feed it back via configuration
  on the next launch.

Default `MarkdownEditorConfiguration.spellChecking` keeps the previous
all-on behavior, so existing callers see no change.

Quote substitution has the same caret-movement bug pattern and is
deliberately left for a follow-up issue.
@Nicolas-Py Nicolas-Py requested a review from luca-chen198 May 19, 2026 11:15
@Nicolas-Py Nicolas-Py marked this pull request as ready for review May 19, 2026 11:15
Copy link
Copy Markdown
Collaborator

@luca-chen198 luca-chen198 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

gj

@luca-chen198 luca-chen198 merged commit beabeb0 into main May 19, 2026
1 check passed
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.

Spell-check toggle doesn't persist — coordinator resets it on caret movement

2 participants