CM6 dynamically switching syntax theme w/ reconfigure

Hi. I’m trying to dynamically switch the theme with reconfigure(). I find that the view theme and the mode switch, but not the highlight theme, until I edit the text.

Here is an example. When rebuild is on, it creates a new CodeMirror view instance, and switching the theme switches the view theme and the highlight theme.

When rebuild is off, it tries to do a full reconfigure. It switches the language and the view theme just fine, but the highlight theme doesn’t change until I edit the text.

Is there something I should do to get it to update the highlighting, or might this be a bug? I tried adding changes and that works, but only if I actually change the text. I looked for a way to refresh and redraw, and didn’t find any. That’s probably best, but without a manual redraw, automatic redraw will need to work, or I will just need to recreate the view (which I am doing for now).

The code is also here:

import React, { useState, useRef, useEffect } from "react";
import {
  EditorView,
  keymap,
} from '@codemirror/view'
import { EditorState } from '@codemirror/state'
import { history, historyKeymap } from '@codemirror/history'
import { indentOnInput, LanguageSupport } from '@codemirror/language'
import { defaultKeymap } from '@codemirror/commands'
import { javascriptLanguage } from '@codemirror/lang-javascript'
import { pythonLanguage } from '@codemirror/lang-python'
import { defaultHighlightStyle } from '@codemirror/highlight'
import {
  oneDarkTheme,
  oneDarkHighlightStyle
} from '@codemirror/theme-one-dark'
import pickBy from 'lodash/pickBy'
import "./style.css";

const languageExtensions = {
  javascript: [new LanguageSupport(javascriptLanguage)],
  python: [new LanguageSupport(pythonLanguage)],
}

const themeExtensions = {
  light: [defaultHighlightStyle],
  dark: [oneDarkTheme, oneDarkHighlightStyle]
}

const exampleCode = `// JavaScript line comment
# Python line comment

for (let i=1; i <= 100; i++) {
  let s = '';
  if (i % 3 == 0) s += 'Fizz';
  if (i % 5 == 0) s += 'Buzz';
  console.log(s.length ? s : i);
}

for i in range(50):
    print(i)`;

export default function App() {
  const [rebuild, setRebuild] = useState(true)
  const [language, setLanguage] = useState('javascript');
  const [theme, setTheme] = useState('light');
  const container = useRef(null);
  const editor = useRef(null);

  useEffect(() => {
    if (container.current) {
      const extensions = [
        history(),
        indentOnInput(),
        keymap.of([
          ...defaultKeymap,
          ...historyKeymap,
        ]),
        ...languageExtensions[language],
        ...themeExtensions[theme],
      ]
      if (!editor.current) {
        editor.current = new EditorView({
          state: EditorState.create({
            doc: exampleCode,
            extensions,
          }),
          parent: container.current,
        })
      } else if (rebuild) {
        const doc = editor.current.state.doc
        editor.current.destroy()
        editor.current = new EditorView({
          state: EditorState.create({
            doc: exampleCode,
            extensions,
          }),
          parent: container.current,
        })
      } else {
        editor.current.dispatch({
          reconfigure: {
            full: extensions,
          }
        })
      }
    }
  }, [rebuild, language, theme, container, editor])

  return (
    <div>
      <p className="buttons">
      rebuild {rebuild ? 'on' : 'off'}{' '}
        <button onClick={() => setRebuild(true)}>On</button>
        <button onClick={() => setRebuild(false)}>Off</button>
      </p>
      <p className="buttons">
      {language}{' '}
        <button onClick={() => setLanguage('javascript')}>JavaScript</button>
        <button onClick={() => setLanguage('python')}>Python</button>
      </p>
      <p className="buttons">
        {theme}{' '}
        <button onClick={() => setTheme('light')}>Light</button>
        <button onClick={() => setTheme('dark')}>Dark</button>
      </p>
      <div className="code" ref={container}></div>
    </div>
  );
}

Thanks!

1 Like

Yes, that was definitely a bug. @codemirror/highlight 0.17.2 should work better.

1 Like

Thanks! I updated the example and confirmed that it works!

I’m thinking of building a playground for CodeMirror 6. It would be the only current one that I know of that allows trying out different themes and different syntaxes. Would you want to review it before I blog about it, or prefer to get an official one built first?

It’s possible to do that with two code themes and a variety of languages in https://console.resources.co/ (or with only two languages in my above example), but it isn’t presented as a playground and doesn’t document how to do it (though the code is open source).

Trying out the different syntaxes on console.resources.co paste this into the console and press enter:
{
  "js": "export default () => {\n  console.log(\"Hello, World\")\n}"
}

Then click the gray bubble that says js with the left mouse button. A menu will appear. Select View > Code > JavaScript.

Reproducing the old bug 🐛To reproduce the old bug, you'll need to grab the code and set up a build that has the old version of `@codemirror/highlight`, either by downloading it and running it yourself or by upgrading to the paid version of StackBlitz.

I’ll probably get around to a playground on the website at some point, but if you want to set one up already that’d be cool. And feel free to blog!

1 Like

Nice, this is very good idea.

1 Like