Instructions for migrating a mode from CM5->CM6?

I had previously created a custom mode for syntax highlighting for the ChordPro (https://www.chordpro.org/) syntax - I found the legacy-modes directory and I wonder if there’s any guidance you’d share for how to quickly? easily? migrate a CM5 mode file to CM6?

I’d love to contribute and continue to improve this for the public OSS modes.

Here was my mode file before:

  require("codemirror/addon/mode/simple");
  const chordBases = ["A", "B", "C", "D", "E", "F", "G"];
  const chordStartMatches = chordBases.map(chord => ({
    regex: new RegExp(`\\[${chord}`, "i"),
    push: chord,
    token: `chord-${chord}`,
  }));
  let chordStates = {};
  chordBases.map(chord => {
    chordStates[chord] = [
      { regex: /\]/, pop: true, token: `chord-${chord}` },
      { regex: /[^\]]+/, token: `chord-${chord}` },
    ];
  });
  //console.debug("chordStartMatches", chordStartMatches, chordStates);
  codemirror.defineSimpleMode("chordpro", {
    start: [
      ...chordStartMatches,
      { regex: /\[/, push: "chord", token: "chord" },
      { regex: /\{/, push: "directive", token: "directive" },
    ],
    chord: [
      { regex: /\]/, pop: true, token: "chord" },
      { regex: /\w+/, token: "chord" },
    ],
    ...chordStates,
    directive: [
      { regex: /\}/, pop: true, token: "directive" },
      { regex: /[\w\s:]+/, token: "directive" },
    ],
    comment: [],
    meta: {},
  });

Here is an example of a CM6-style legacy mode using simple-mode. You’d require @codemirror/legacy-modes/mode/simple-mode.js instead in a file that lives in a different package.

1 Like

I rewrote my mode file and then set an extension for my CodeMirror instance ala StreamLanguage.define(chordPro) but I’m not seeing classNames or other bits I can use to syntax highlight/style my tokens.
In the rust example something must be setting up the highlight style somewhere and I need to do something special to write and load a highlighter? Any examples of how I can do that in userland would be sweet!

Thank you so much

import { simpleMode } from "@codemirror/legacy-modes/mode/simple-mode";

const chordBases = ["A", "B", "C", "D", "E", "F", "G"];
const chordStartMatches = chordBases.map(chord => ({
  regex: new RegExp(`\\[${chord}`, "i"),
  push: chord,
  token: `chord-${chord}`,
}));
let chordStates = {};
chordBases.map(chord => {
  chordStates[chord] = [
    { regex: /\]/, pop: true, token: `chord-${chord}` },
    { regex: /[^\]]+/, token: `chord-${chord}` },
  ];
});

export const chordPro = simpleMode({
  start: [
    ...chordStartMatches,
    { regex: /\[/, push: "chord", token: "chord" },
    { regex: /\{/, push: "directive", token: "directive" },
  ],
  chord: [
    { regex: /\]/, pop: true, token: "chord" },
    { regex: /\w+/, token: "chord" },
  ],
  ...chordStates,
  directive: [
    { regex: /\}/, pop: true, token: "directive" },
    { regex: /[\w\s:]+/, token: "directive" },
  ],
  languageData: {
    name: "chordPro",
  },
});

You’re returning token names that don’t exists as syntax tags, and as such won’t be styled. See the docs.

There’s a link there not working for me - pointing to https://lezer.codemirror.net/docs/ref#highlight.tags - looks like a DNS err?

Someone is running a DDoS against my DNS service targeting codemirror.net subdomains, for some reason. I hope it will clear up soon. In the meantime, you should be able to access the same content at this URL.

Thank you - I think I do need to define some custom tags because I want to distinguish chords from one another and set the colors programmatically. I’ve read through the various docs on highlighting and StreamLanguage but I haven’t found the incantation for how I might go about defining these tags in a simpleMode export + StreamLanguage.define(mySimpleMode) use pattern … I tried dropping tokenTable in my simpleMode object and highlightStyle there, but if there’s an example out there that comes to mind that would be lovely.

aka I’d found CodeMirror 6: stream-syntax with custom tags but that last comment is missing the context I think I’d need and that syntax for tokenTable isn’t what simpleMode() seems to want

Example of what I tried by inferring from VSCode Rule[] guidance

Also tried a bunch of Github results but not seeing anyone with a programmatic token https://github.com/search?q=%40codemirror%2Flegacy-modes%2Fmode%2Fsimple-mode+highlight&type=code

Right, simpleMode has no mechanism for adding additional properties to the StreamParser object it returns, so you’d have to call it, and then assign to .tokenTable to add that.

Got it!
Leaving this here in case anyone else comes looking for a contained example:

import {
  HighlightStyle,
  LanguageSupport,
  StreamLanguage,
  syntaxHighlighting,
} from "@codemirror/language";
import { simpleMode } from "@codemirror/legacy-modes/mode/simple-mode";
import { Tag } from "@lezer/highlight";

const chordBases = ["A", "B", "C", "D", "E", "F", "G"];
const chordStartMatches = chordBases.map(chord => ({
  regex: new RegExp(`\\[${chord}`, "i"),
  push: chord,
  token: `chord-${chord}`,
}));
let chordStates = {};
chordBases.forEach(chord => {
  chordStates[chord] = [
    { regex: /\]/, pop: true, token: `chord-${chord}` },
    { regex: /[^\]]+/, token: `chord-${chord}` },
  ];
});

const chordProSimpleMode = simpleMode({
  start: [
    ...chordStartMatches,
    { regex: /\[/, push: "chord", token: "propertyName" },
    { regex: /\{/, push: "directive", token: "comment" },
  ],
  chord: [
    { regex: /\]/, pop: true, token: "propertyName" },
    { regex: /\w+/, token: "propertyName" },
  ],
  ...chordStates,
  directive: [
    { regex: /\}/, pop: true, token: "comment" },
    { regex: /[\w\s:]+/, token: "comment" },
  ],
  languageData: {
    name: "chordPro",
  },
});

/* @ts-ignore */
chordProSimpleMode.tokenTable = {};
chordBases.forEach(chord => {
  chordProSimpleMode.tokenTable[`chord-${chord}`] = Tag.define();
});

const chordProModeLanguage = StreamLanguage.define(chordProSimpleMode);

export const chordProHighlighter = syntaxHighlighting(
  HighlightStyle.define(
    chordBases.map(chord => ({
      tag: chordProSimpleMode.tokenTable[`chord-${chord}`],
      class: `cm-chord-${chord}`,
    })),
  ),
);

const chordProMode = new LanguageSupport(chordProModeLanguage, [
  chordProHighlighter,
]);
export default chordProMode;