**(at the bottom are what I suspect are the simplest steps to repeat, but including additional info in case you find it useful)
I’m using it with VueJS. Below I removed most of the interactions with props and external components, etc… since it seems to be throwing the same error just changing the code with a test button I inserted. I’m using a debounce with the updateListener.of but I’m pretty sure it has nothing to do with that.
Also note:
- the doc I set to props.code, but it happens just the same if I use standard text
- interestingly when I try to select anything or move the caret in the rows that were changed, it throws the error. If I press enter and type new text and try to select it, then it correctly moves the caret with no errors as I would expect.
- After playing with it a bit I’m suspecting there is an issue when code gets inserted there may be cases where internal positioning states are not correctly updating to accommodate the newly inserted code
I’m using the following versions:
“@codemirror/autocomplete”: “^0.18.7”,
“@codemirror/basic-setup”: “^0.18.2”,
“@codemirror/closebrackets”: “^0.18.0”,
“@codemirror/commands”: “^0.18.3”,
“@codemirror/comment”: “^0.18.1”,
“@codemirror/fold”: “^0.18.1”,
“@codemirror/gutter”: “^0.18.4”,
“@codemirror/highlight”: “^0.18.4”,
“@codemirror/history”: “^0.18.1”,
“@codemirror/lang-css”: “^0.18.0”,
“@codemirror/lang-html”: “^0.18.1”,
“@codemirror/lang-javascript”: “^0.18.0”,
“@codemirror/language”: “^0.18.2”,
“@codemirror/lint”: “^0.18.4”,
“@codemirror/matchbrackets”: “^0.18.0”,
“@codemirror/rectangular-selection”: “^0.18.0”,
“@codemirror/search”: “^0.18.4”,
“@codemirror/state”: “^0.18.7”,
“@codemirror/view”: “^0.18.17”,
“vue”: “^3.1.1”,
<template>
<button @click="testcodechange">Change Content</button>
<div :id="my-editor" class="cm-editor-wrapper" style="width:100%;height:100%"></div>
</template>
<script>
import { ref, onMounted, watch} from 'vue';
import { debounce } from '@/embedded_assets/js/debounce';
import { EditorView, ViewUpdate, keymap, highlightSpecialChars, drawSelection, highlightActiveLine } from "@codemirror/view";
import { EditorState, Extension, Compartment, EditorSelection } from "@codemirror/state";
import { history, historyKeymap } from "@codemirror/history"
import { foldGutter, foldKeymap } from "@codemirror/fold"
import { indentOnInput } from "@codemirror/language"
import { lineNumbers, highlightActiveLineGutter } from "@codemirror/gutter"
import { defaultKeymap, defaultTabBinding } from "@codemirror/commands"
import { bracketMatching } from "@codemirror/matchbrackets"
import { closeBrackets, closeBracketsKeymap } from "@codemirror/closebrackets"
import { searchKeymap, highlightSelectionMatches } from "@codemirror/search"
import { autocompletion, completionKeymap } from "@codemirror/autocomplete"
import { commentKeymap } from "@codemirror/comment"
import { rectangularSelection } from "@codemirror/rectangular-selection"
import { defaultHighlightStyle } from "@codemirror/highlight"
import { lintKeymap } from "@codemirror/lint"
import { html } from "@codemirror/lang-html";
import { defaultLight } from "./themes/default-light";
export default {
props:{
code:{
default: "<p>Hello World</p>"
},
debounceTime:{
default: 0
}
},
setup(props, ctx){
//Create the debounce function for emitting updates out of the editor
const debouncedEmitter = debounce((v)=>{
if(props.code != v.state.doc.toString()){
ctx.emit("update:code", v.state.doc.toString());
}
}, props.debounceTime);
//Generate the editor
const CMEditor = ref(null);
//Must be on mount because it needs to get attached to the parent which is not
//generated prior to setup running
onMounted(()=>{
CMEditor.value = new EditorView({
parent: document.getElementById("my-editor"),
state: EditorState.create({
doc: props.code,
extensions:[
lineNumbers(),
EditorView.updateListener.of(debouncedEmitter),
highlightActiveLineGutter(),
highlightSpecialChars(),
history(),
foldGutter(),
drawSelection(),
EditorState.allowMultipleSelections.of(false),
indentOnInput(),
defaultHighlightStyle.fallback,
bracketMatching(),
closeBrackets(),
autocompletion(),
rectangularSelection(),
highlightActiveLine(),
highlightSelectionMatches(),
keymap.of([
...closeBracketsKeymap,
...defaultKeymap,
...searchKeymap,
...historyKeymap,
...foldKeymap,
...commentKeymap,
...completionKeymap,
...lintKeymap,
defaultTabBinding
]),
html(),
//theme
defaultLight,
]
})
});
})
//needs to reset cursor position on update because it will throw an error
const updateCode = (newCode) => {
const codeTransaction = CMEditor.value.state.update({
changes: {
from: 0,
to: CMEditor.value.state.doc.length,
insert:newCode,
},
});
CMEditor.value.update([codeTransaction]);
}
//code to run for testing purposes
const testcodechange = () => {
console.log("changing");
updateCode("test");
}
return { CMEditor, updateCode, testcodechange};
},
}
</script>
<style>
.cm-editor-wrapper > .cm-editor{
width: 100%;
height: 100%;
}
.cm-editor-wrapper .cm-scroller {
scrollbar-width: 15px;
}
</style>
Boiling it down:
I think all that’s required is:
- Create an editor
- Set the initial state doc using a string or variable
- Create a code transaction (I did it using the entire size of the editor but I suspect it will work regardless of size) using from, to, insert
- Update the editor using the code transaction
- Try either selecting text or moving the caret anywhere in the rows with the inserted code