By qbane (the thread 🧵)
2022/8
Update 2023/8/18
The author has found a leaner approach: https://github.com/PuruVJ/neocodemirror.
tl;dr. It provides an action and let you optionally create a specific store for passing in. The data would be notified via the store only if a store is present.Comment: The "use:action" syntax may be more mighty than one might have thought!
Minimal PoC: https://svelte.dev/repl/dfa4759d5bdc46a4ae4a18a6c71ade17?version=3.48.0
First thoughts:
<textarea>
but fancier! Get/set its state in Svelte's pattern.on:
bound events?We can see from the docs:
Text
{anchor, head?}
basicSetup
or minimalSetup
.(trs, view) => view.update(trs)
.Some insights of mine:
setState
", reconfigure
, …). CodeMirror does have it's own lifecycle and updating the view wrongly will throw errors.selection
depends on the doc
. Making it reactive to doc
change is convoluted. I am not going to implement it as a prop.parent
and root
for making bindings for Svelte.dispatch
to pull off the reactivity. It is crucial that this is synchronous, as opposed to the one EditorView.updateListener
(receives a ViewUpdate
with zero or more transactions), which is asynchronous. Svelte's store contract also mandates some operations to be synchorous.Making doc
bindable may harm the performance! To make it reactive, we have to monitor updates to the editor and extract the string (.toString()
) in bulk at every trigger. Furthermore, the redundant string would linger in the prop. Better implement it as a store to read (and also cache) the content on demand.
A "bindable" value is a nice-to-have if the user thinks the performance impact is tolerable. And this is more like a traditional <textarea>
. CM 5 did have this feature via fromTextArea
. The abovementioned store is pretty neat for our need. The "on demand" means that the extra work is hidden until the store has at least one subscription.
function editorTxHandler(tr) {
this.update([tr])
/* ... */
if (tr.docChanged) {
__docCached = null
if (subscribers.size > 0) { // <---
updateDocStore(__docCached = tr.newDoc.toString())
}
dispatch('change', {view: this, tr})
}
}
TODO: How to handle the undo history when the store itself is updated?
If you try making a browser-native input field or textarea with ad-hoc text-editing commands, you will be surprised that the undo history is sometimes completely erased by a single programmatic update foo.value = 'bar'
and there is no way to recover it.
But here, the default behavior will leave a trace each time the store is written. We can choose what to do with the undo stack CM provides.
https://svelte.dev/repl/91649ba3e0ce4122b3b34f3a95a00104?version=3.49.0