Problem: `extmark_splice()` was being called before `ml_replace()`,
which caused the on_bytes callback to be invoked with the old buffer
text instead of the new text.
Solution: store metadata for each match in a growing array, call
`ml_replace()` once to update the buffer, then call `extmark_splice()`
once per match.
Closes https://github.com/neovim/neovim/issues/36370.