fix(muya): keep the search highlight across a full re-render (#5651) by Jocs · Pull Request #5652 · marktext/marktext · GitHub
Skip to content

fix(muya): keep the search highlight across a full re-render (#5651) - #5652

Merged
Jocs merged 5 commits into
developfrom
fix/5651-search-highlight-survives-rerender
Oct 8, 2026
Merged

Jocs merged 5 commits into
developfrom
fix/5651-search-highlight-survives-rerender

Conversation

@Jocs

@Jocs Jocs commented Oct 7, 2026

Copy link
Copy Markdown
Member

Closes #5651

Summary

setOptions(options, true) and locale() re-render every block through Muya._forceRender(), which rebuilt the tree but never re-applied the active search. The find bar kept showing its count while every mu-highlight / mu-selection span disappeared — for plain word searches and regexp searches alike. So toggling e.g. Preferences → Markdown → "将软换行视为空格" mid-search wiped the highlight.

Search.refresh() now re-runs the remembered query against the rebuilt blocks and keeps the active match where it was. The block that also holds the caret is rendered once with both the cursor and its highlights — rendering it twice lets the highlight and the caret overwrite each other — so _forceRender restores only the DOM selection.

Type of change

  • Bug fix (non-breaking, fixes an issue)
  • New feature (non-breaking, adds functionality)
  • Breaking change (causes existing functionality to change)
  • Documentation update

Test plan

  • New tests added (or explain why not needed)

  • Manually tested on:

  • Unit (src/search/__tests__/searchSurvivesRerender.spec.ts): a word search keeps its highlight and active index across setOptions(..., true); the rebuilt blocks are the ones re-highlighted; a closed find bar is not resurrected; the caret survives when its block also holds the active match.

  • E2E (e2e/tests/editing/search-rerender-highlight.spec.ts): the same two scenarios in a real browser, asserting both the highlight spans and window.getSelection().

  • Both specs fail without the fix (verified by stashing the source changes).

  • pnpm -C packages/muya exec vitest run — 2433 passing; lint / lint:types clean; tests/editing e2e green.

Notes for reviewers [optional]

The search highlight is re-applied only when a query is active, so _forceRender keeps its old behaviour when the find bar is closed. _options stores the last query's options so the re-run reproduces the original flags (case sensitivity, whole word, regexp).


By submitting this pull request, I confirm that my contribution is made under the terms of the MIT license.

Jocs added 3 commits October 8, 2026 01:43
`setOptions(options, true)` and `locale()` re-render every block through
`Muya._forceRender()`, which rebuilt the tree without re-applying the
active search. The find bar kept its count while every highlight span
disappeared, for plain word searches and regexp searches alike.

`Search.refresh()` now re-runs the remembered query on the new blocks and
keeps the active match where it was. The block that also holds the caret
is rendered once with both the cursor and its highlights — rendering it
twice lets the highlight and the caret overwrite each other — leaving
`_forceRender` to restore just the DOM selection.
Keep only the two comments the code cannot state on its own (why the
caret is handed to `refresh`, why the stale matches are cleared first).
@Jocs
Jocs requested a balanced review from Copilot October 7, 2026 18:16

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟡 Changes recommended

Refreshed result counts can leave an invalid active index and are not synchronized with the desktop find UI.

2 open findings
What changed in this PR

Fixes search highlights disappearing after Muya performs a full re-render.

Changes:

  • Re-runs active searches after full rendering.
  • Preserves highlights, active match, and caret.
  • Adds unit and browser regression tests.
File Description
packages/​muya/​src/​search/​index.ts Stores search options and refreshes matches.
packages/​muya/​src/​muya.ts Restores search highlights and cursor together.
packages/​muya/​src/​search/​__tests__/​searchSurvivesRerender.spec.ts Adds unit regression coverage.
packages/​muya/​e2e/​tests/​editing/​search-rerender-highlight.spec.ts Adds browser-level coverage.

🧠 Review effort: Balanced


💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread packages/muya/src/muya.ts Outdated
const cursor: IRenderCursor | undefined = cursorBlock
? { block: cursorBlock, anchor: { offset: begin }, focus: { offset: end } }
: undefined;
this.editor.searchModule.refresh(cursor);
Comment thread packages/muya/src/search/index.ts Outdated
Comment on lines +253 to +257
this.search(value, {
...this._options,
highlightIndex: index >= 0 && index < matches.length ? index : -1,
selectHighlight: false,
});
)

`Search.refresh()` re-ran the query but validated the active index against
the matches it had just discarded, and it only updated the engine's live
instance. The find bar renders a snapshot its own handlers own, so a
parse-affecting toggle (e.g. `multilineBlockquote`, which stops `>>>` from
being text) could leave a stale count and an index past the end — `1 / 0`.

Clamp the index against the matches the new parse produced and emit
`search-refreshed` so the desktop replaces its snapshot.
@github-actions

github-actions Bot commented Oct 7, 2026 •

Copy link
Copy Markdown
Contributor

Build artifacts for PR #5652:

Run: https://github.com/marktext/marktext/actions/runs/37734779724

Artifact Size Link
marktext-macos-arm64 285.7 MB Download
marktext-linux 635.0 MB Download
marktext-windows-arm64 278.2 MB Download
marktext-macos-x64 296.0 MB Download
marktext-windows-x64 286.4 MB Download

@Jocs
Jocs force-pushed the fix/5651-search-highlight-survives-rerender branch from 15bab43 to 22844ea Compare October 8, 2026 05:51
Review of the refresh rework turned up two defects:

- `search()` trusts `highlightIndex` unconditionally, but callers pass one
  from before the text changed — `replace()` rewrites the text and
  `refresh()` re-parses it — so a shrunk result set left the index past the
  end (`1 / 0` in the find bar, no active highlight). Clamp it to the last
  match the search produced, which covers both callers; `matches.length - 1`
  is already -1 when nothing matched.
- `Muya._forceRender()` stopped re-rendering the caret's block, so the
  syntax markers around the caret stayed collapsed after a preference
  toggle. Restore that render, after the search refresh so the highlight
  pass cannot drop it again.

Tests: the caret case no longer asserts a highlight survives in the caret's
block (the caret render replaces it), and the e2e spec whose cases the unit
spec already covered is gone.

Also folds in the find-bar tidy-up (one `emptySearch()` default, dead
comment removal), two desktop typo fixes and cSpell words.
@Jocs
Jocs force-pushed the fix/5651-search-highlight-survives-rerender branch from 22844ea to 280d655 Compare October 8, 2026 05:54
@Jocs
Jocs merged commit ad1e1cf into develop Oct 8, 2026
17 checks passed
@Jocs
Jocs deleted the fix/5651-search-highlight-survives-rerender branch October 8, 2026 06:11
csells added a commit to csells/MarkText that referenced this pull request Oct 13, 2026
…nder

Upstream marktext#5651 (marktext#5652) re-runs Muya's search after a forced render and
refreshes the find bar through `search-refreshed`. Core find paints its
own highlights, so Muya now emits `force-rendered` and the editor repaints
the current result on the re-created blocks. Stepping through matches
also records the active match in the search snapshot, which a
parse-affecting preference's refresh and Replace One act on; both used
the first match before.
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.

[Bug] Toggling a rendering preference clears the active search highlight

2 participants