found issue with MergeView

@marijn Hello! I’m using the latest version Merge plugin with Vue3.0, the diff function seems not working well when I use YAML or XML file. two panel with same content, but it always verdict the whole b panel was changed.

And a crush happened when I click the revert button or type in b panel ,

here is my code

  <div ref="Cm" style="width: 100%; height: 100%"></div>

<script setup lang="ts">
import { basicSetup } from 'codemirror'
import { indentWithTab } from '@codemirror/commands'
import { MergeView } from '@codemirror/merge'
import { onMounted, onBeforeUnmount, watch } from 'vue'
import { dracula } from '@ddietr/codemirror-themes/dracula'
import { keymap } from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { json, jsonParseLinter } from '@codemirror/lang-json'
import { linter, lintGutter } from '@codemirror/lint'
import { StreamLanguage } from '@codemirror/language'
import { yaml } from '@codemirror/legacy-modes/mode/yaml'

const Cm = $ref<any>()
const props = defineProps({
  oText: {
    type: String,
    required: true,
  mText: {
    type: String,
    required: true,
let eView = $ref<MergeView>()

const initEditor = () => {
  eView = new MergeView({
    a: {
      extensions: [basicSetup, dracula, EditorState.readOnly.of(true)],
      doc: props.oText,
    b: {
      extensions: [
        // StreamLanguage.define(yaml),
        // json(),
        // linter(jsonParseLinter()),
        // lintGutter(),
      doc: props.mText,
    parent: Cm,
    revertControls: 'a-to-b',
const getDoc = () => {
  return eView?.b.state.doc.toString()

onBeforeUnmount(() => {

onMounted(() => {

watch([() => props.mText], () => {
  eView?.b.dispatch({ changes: { from: 0, to: eView.b.state.doc.length, insert: props.mText } })
watch([() => props.oText], () => {
  eView?.a.dispatch({ changes: { from: 0, to: eView.a.state.doc.length, insert: props.oText } })
defineExpose({ getDoc })

two problem only happened with YAML and XML, other languages are fine.
please help me resolve it.

The merge module shouldn’t interact with the language or syntax tree at all, so I’m not sure how this can be language-related. Trying to set up a merge view with XML content doesn’t show this happening for me. Can you provide an example script?

You are right, the issue is not related with language at all. I test the issue again, and find out that It would always happen as long as the content ends with a linebreak ‘\n’.

I’m not seeing anything like this happen with content that ends with a linebreak. Please provide precise (preferably minimal) example documents needed to reproduce this.

Try this content
‘[\n {\n “targets”: [\n “”\n ],\n “labels”: {\n “milepost”: “K123+102”\n }\n }\n]\n’

I might know why you can’t reproduce the issue. In my code above, I init these panel with two empty string, then insert this content after request. Somehow the b panel was inserted before a, then the issue show up. You can reproduce it by adding two methods below, execute test1 after test2.

const test1 = () => {
    changes: {
      from: 0,
      to: eView.a.state.doc.length,
        '[\n  {\n    "targets": [\n      ""\n    ],\n    "labels": {\n      "milepost": "K123+102"\n    }\n  }\n]\n',
const test2 = () => {
    changes: {
      from: 0,
      to: eView.b.state.doc.length,
        '[\n  {\n    "targets": [\n      ""\n    ],\n    "labels": {\n      "milepost": "K123+102"\n    }\n  }\n]\n',

My thought was It might be caused by two chunks mixing ,that panel a was inserted, and the former chunk still remain.

But still need the linebreak at the end.
made a example

That helped reproduce this. This patch should fix it.

Great work, problem solved. Thx.