CodeMirror in the WordPress Code Editor.
I actually got it to work!
I spent some time getting CodeMirror to take over the <textarea> in the code editor mode of WordPress. As of posting, I have it mostly working.
In WordPress there is a “Theme file editor” page in the admin-side of things, and it lets you edit theme files. Javascript, PHP, CSS, HTML, it can edit these files with a fancy editor (CodeMirror) that highlights syntax. I write HTML in a different part of WordPress, in the post editor, in code editor mode. The default visual mode, I don’t use, because I gave up on the craziness of blocks and stick to writing my own code. The C in CMS is content, not bloCks. But the code editor doesn’t support the highlighter, because why would it be consistent?
The fancy editor runs with Javascript and styles. To invoke it, it must be included in the page, and we’ll need to invoke it. To include it we want to keep things tidy, so we first add a helper to call our includer function in PHP:
add_action( 'admin_enqueue_scripts', 'codemirror_enqueue_scripts' );functions.php, used during setup to call the including function below at the appropriate stage.Here’s the includer function that one calls:
function codemirror_enqueue_scripts( $hook ) {
if ( 'post.php' != $hook && 'post-new.php' != $hook ) {
return;
}
wp_enqueue_code_editor(
array(
'type' => 'text/html'
)
);
wp_enqueue_style( 'wp-codemirror' );
// You could wp_enqueue_style a custom theme stylesheet here.
wp_enqueue_script(
'cm-edit',
get_template_directory_uri() . '/inc/cm_edit.js',
array(),
false,
array(
'strategy' => 'defer',
'in_footer' => true
));
}cm_edit.js).Now we have to actually kick it off inside the editor page once it loads. For that we need the support script, cm_edit.js:
var gb_cm;
const gb_cm_conf = {
"mode": "htmlmixed",
// "theme": "custom-theme-name",
"lineWrapping": true,
"lineNumbers": true,
"extraKeys": {
Tab: (cm) => {
let spaces = " ".repeat(cm.getOption("indentUnit") + 1);
cm.replaceSelection(spaces);
}
}
};
/* FIXME: We would prefer to wait for an event over setInterval(). */
window.addEventListener("load", (event) => {
let editorChecker = setInterval(() => {
let target = document.getElementById('post-content-0');
if (target) {
clearInterval(editorChecker);
const type = wp.data.select('core/editor').getCurrentPostType();
const id = wp.data.select('core/editor').getCurrentPostId();
gb_cm = wp.CodeMirror.fromTextArea(target, gb_cm_conf);
gb_cm.on("change", (change) => {
gb_cm.save();
wp.data.dispatch('core').editEntityRecord(
'postType',
type,
id,
{
content: target.value,
blocks: undefined,
selection: undefined
});
});
}
}, 500);
});cm_edit.js.Note that I had to use setInterval because I could not find a better way to ensure the page’s environment was far enough on to invoke CodeMirror. The Javascript side of WordPress is much harder to follow, compared to PHP. There are whispers of maybe-useful events, but the few I found fired too early to be useful. I gave up trying to find a good one and went with polling.
If you install the above bits in the right places, and you open a post in WordPress (in code editor), you will see the <textarea> be replaced by a syntax-highlighted version. Here’s the bit where I had trouble: getting it to register that it’s changed with whatever script is responsible for noticing that it’s changed.
Eventually, I found the post text editor component of the editor package (see @wordpress/core-data: editEntityRecord), which threw out several attempts to use the blocks API to replace the content. The problem with those was it kept reformatting some of the content (removing blank lines, etc.), where I want it to remain as entered. The post text editor method does the right thing.
Right now, there’s zero support for switching it on and off, or going into visual editor mode, etc. I probably won’t change that. I don’t use it, and it would only try to blockify things if I did. But if you want to use the code and need those parts, you’d need to add them.
One of my big goals in implementing it this way is to keep changes and code minimal, so that it does what I want without needing a lot of interaction with lots of the WordPress client-side code.
I’m hoping that WordPress will eventually add the functionality as first-class. I did learn that at some point there will be a CodeMirror version six, which perhaps might mean that it will be integrated? Perhaps not.
Syntax-highlighting is nice to have. WordPress has focused a lot more on wuzzywig, which is fine for those who need it. But so long as the language of the web remains HTML, I’d rather write HTML.