Search code examples
vuejs3codemirror

CodeMirror on Vue3 has a problem when setValue is kicked


I'm trying to use CodeMirror on Vue3 and the problem occurs when I call doc.setValue().

The Problem is following:

  1. Cursor position is broken when doc.setValue() is called enter image description here
  2. CodeMirror throws an exception when continuing editing

The exception is here.

Uncaught TypeError: Cannot read property 'height' of undefined
    at lineLength (codemirror.js:1653)
    at codemirror.js:5459
    at LeafChunk.iterN (codemirror.js:5623)
    at Doc.iterN (codemirror.js:5725)
    at Doc.iter (codemirror.js:6111)
    at makeChangeSingleDocInEditor (codemirror.js:5458)
    at makeChangeSingleDoc (codemirror.js:5428)
    at makeChangeInner (codemirror.js:5297)
    at makeChange (codemirror.js:5288)
    at replaceRange (codemirror.js:5502)

How should I solve this?


~~~

Versions are CodeMirror: 5.61.1, Vue.js: 3.0.11

My code is following:

index.html

<div id="app"></div>
<script src="./index.js"></script>

index.js

import { createApp } from 'vue';
import App from './App';

const app = createApp(App);

app.mount('#app');

App.vue

<template>
    <div>
        <button @click="click">Push Me</button>
        <textarea id="codemirror"></textarea>
    </div>
</template>
<script>
import CodeMirror from 'codemirror/lib/codemirror.js';
import 'codemirror/lib/codemirror.css';

// import codemirror resources
import 'codemirror/addon/mode/overlay.js';
import 'codemirror/mode/markdown/markdown.js';
import 'codemirror/mode/gfm/gfm.js';

export default {
    data () {
        return {
            cm: null
        }
    },
    mounted () {
        this.cm = CodeMirror.fromTextArea(document.getElementById('codemirror'), {
            mode: 'gfm',
            lineNumbers: true,
        });
    },
    methods: {
        click (event) {
            this.cm.getDoc().setValue('foo\nbar');
        }
    }
}
</script>

Thanks.

UPDATES

First, this problem also occurs when I used replaceRange() with multiline.

Unfortunately, I couldn't find any solution. So I tried to find another way.

My solution is recreating Codemirror instance with a textarea that has new content.

It works well.

// Remove old editor
this.cm.toTextArea();

// Get textarea
const textarea = document.getElementById('codemirror');
// Set new content
textarea.value = 'foo\nbar';
// Create new editor
this.cm = CodeMirror.fromTextArea(textarea, { /** options */ });

Solution

  • I found a method, you can use toRaw to get the original Object from Proxy,and this method can be also used in monaco-editor

    import { toRaw } from 'vue'
    import CodeMirror from 'codemirror/lib/codemirror.js';
    import 'codemirror/lib/codemirror.css';
    
    // import codemirror resources
    import 'codemirror/addon/mode/overlay.js';
    import 'codemirror/mode/markdown/markdown.js';
    import 'codemirror/mode/gfm/gfm.js';
    
    export default {
        data () {
            return {
                cm: null
            }
        },
        mounted () {
            this.cm = CodeMirror.fromTextArea(document.getElementById('codemirror'), {
                mode: 'gfm',
                lineNumbers: true,
            });
        },
        methods: {
            click (event) {
                toRaw(this.cm).setValue('foo\nbar');
            }
        }
    }
    

    Another way,you don't have to define cm in data, just use this.cm

    data () {
        return {
            //cm: null
        }
    },