Skip to content

feat(muya): add spellcheck word-replacement API#4392

Merged
Jocs merged 2 commits into
developfrom
feat/muya-spellcheck-replace
Jun 8, 2026
Merged

feat(muya): add spellcheck word-replacement API#4392
Jocs merged 2 commits into
developfrom
feat/muya-spellcheck-replace

Conversation

@Jocs
Copy link
Copy Markdown
Member

@Jocs Jocs commented Jun 8, 2026

Why

The desktop spell checker shows a context menu of suggestions for a misspelled word; choosing one calls the editor to replace the word at the cursor. Legacy muyajs exposed _replaceCurrentWordInlineUnsafe(word, replacement) for exactly this. @muyajs/core had no equivalent, which blocks the desktop migration off legacy packages/muyajs.

What

Adds Muya.replaceCurrentWordInlineUnsafe(word: string, replacement: string): boolean (packages/muya/src/muya.ts), delegating to a new method of the same name on the active content block (packages/muya/src/block/base/content.ts).

Semantics (faithful to legacy muyajs)

  • Reads the cursor in the active content block and probes the word at the cursor start offset via extractWord, the VSCode-derived word-boundary helper ported from legacy muyajs (lib/contentState/core.js).
  • Unsafe: asserts the word found at the cursor equals the expected word. When it does not match (e.g. a Chromium selection mismatch), the call is a safe no-op and returns false.
  • Replaces the word through the Content text setter, so the edit dispatches a json1 op and stays in sync with document state (it does not bypass state sync).
  • Places the cursor immediately after the replacement.
  • No-ops safely (returns false) when there is no active content block or no cursor.

The public method is inserted immediately after Muya.format(type) to stay clear of parallel edits elsewhere in muya.ts.

Tests

New happy-dom vitest spec packages/muya/src/__tests__/replaceCurrentWord.spec.ts boots a real Muya, sets the cursor inside a known misspelled word, calls the API, and asserts getMarkdown() reflects the replacement (state flushes on rAF, awaited via vi.waitFor). Also covers cursor-after-replacement positioning and the no-op paths (word mismatch, no active block, no cursor).

Verification

All green in packages/muya:

  • pnpm lint — 0 errors
  • pnpm lint:types — clean
  • pnpm check-circular — no circular dependency
  • pnpm test — 441 passed
  • pnpm test:spec — 1347 passed

🤖 Generated with Claude Code

Add `replaceCurrentWordInlineUnsafe(word, replacement)` to @muyajs/core,
restoring the legacy muyajs `_replaceCurrentWordInlineUnsafe` API the
desktop spell checker relies on.

When the user right-clicks a misspelled word, Chromium selects the whole
word; choosing a suggestion from the context menu replaces it inline. The
new method finds the word at the cursor (using the VSCode-derived word
boundaries ported from legacy muyajs), asserts it matches the expected
`word`, replaces it through the Content text setter so the change
dispatches a json edit op, and places the cursor after the replacement.
It is a no-op when there is no active content block / cursor, or when the
word at the cursor does not match (guards a Chromium selection mismatch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 8, 2026 06:02
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR adds a public Muya.replaceCurrentWordInlineUnsafe(word, replacement) API to support the desktop spellchecker’s “replace misspelled word” context-menu action during the ongoing migration from legacy packages/muyajs to @muyajs/core.

Changes:

  • Added Muya.replaceCurrentWordInlineUnsafe(...) delegating to the active Content block.
  • Implemented Content.replaceCurrentWordInlineUnsafe(...) using a legacy-ported extractWord helper and updating text via the existing text setter (json1 op).
  • Added a new happy-dom Vitest spec covering success, cursor positioning, and no-op paths.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 1 comment.

File Description
packages/muya/src/muya.ts Exposes the new public spellcheck word-replacement API on Muya.
packages/muya/src/block/base/content.ts Implements word extraction + replacement logic on the active content block.
packages/muya/src/tests/replaceCurrentWord.spec.ts Adds test coverage for replacement behavior and no-op scenarios.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +23 to +26
// Word boundary regexes ported from legacy muyajs
// (lib/contentState/core.js), which in turn derive from VSCode's wordHelper.
// Used by `extractWord` to find the word at the cursor for spell-check
// replacement.
Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Good catch — fixed in 5273b02. You're right that the legacy extractWord and the WORD_SEPARATORS/WORD_DEFINITION regexes (carrying the VSCode wordHelper attribution) live in packages/muyajs/lib/marktext/spellchecker.js, not lib/contentState/core.js (which only holds the replaceWordInLine range-replacement helper). Both port-source comments at lines 24 and 33 now point to lib/marktext/spellchecker.js.

The `extractWord` helper and the `WORD_SEPARATORS`/`WORD_DEFINITION`
regexes were ported from legacy muyajs `lib/marktext/spellchecker.js`
(which carries the VSCode wordHelper attribution), not from
`lib/contentState/core.js`. `core.js` only holds the `replaceWordInLine`
range-replacement helper, so the previous comments pointed at the wrong
file. Correct both references to ease future maintenance.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@Jocs Jocs merged commit c19ef7f into develop Jun 8, 2026
9 checks passed
@Jocs Jocs deleted the feat/muya-spellcheck-replace branch June 8, 2026 07:11
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.

2 participants