Suggestion: lock text range as read-only to user, get a callback to update it

Greetings. In the last hour or so, I finally figured out how to partially restrict the user from altering lines of code I don’t want them to touch. My use-case is a JavaScript function’s source code, where the preamble and the return value should be constant. In addition, based on a nearby checkbox, one line of code in this preamble must change.

I’ve wanted functionality like this for years. (I’m currently using CodeMirror 5.25.2, but release notes up to 5.32 seem to indicate this hasn’t been added.)

Since I’m not sure if this would qualify as a “bug” or not per the guidelines, and I only have the raw code I used to do this, I figured I’d start here and see if there’s interest in folding something like this into CodeMirror itself.

Here’s the code:

  /**
   * Mark CodeMirror lines "read-only", and get a function that can alter them.
   *
   * @param editor    {CodeMirror} The editor.
   * @param startLine {Integer}    The line index to start at.
   * @param endLine   {Integer}    The line index to end at.
   *
   * @returns {Function(String)} A callback for replacing read-only content.
   *
   * @note CodeMirror uses zero-based indexes for the API, and one-based indexes
   * for the rendering:  the first line appears in gutters as line 1, but in the
   * API, it is line 0.
   */
  getTextLock: function(editor, startLine, endLine)
  {
    const doc = editor.getDoc();
    const options = Object.freeze({ readOnly: true, className: "readOnly" });
    let mark = doc.markText(
      {line: startLine, ch: 0},
      {line: endLine, ch: 0},
      options
    );

    return function(newText) {
      if (typeof newText !== "string")
        throw new Error("text lock only allows replacements with strings");
      if (newText[newText.length - 1] !== "\n")
        newText += "\n";
      const positioning = mark.find();
      mark.clear();
      doc.replaceRange(newText, positioning.from, positioning.to);
      startLine = positioning.from.line;
      endLine = startLine + newText.split("\n").length - 1;
      mark = doc.markText(
        {line: startLine, ch: 0},
        {line: endLine, ch: 0},
        options
      );
    };
  },

Unfortunately, I haven’t figured out how to prevent the user from appending to the very end of the CodeMirror instance, by simply using the Enter key at the end of the editor’s content.

You can add an inclusiveRight: true option to a marker that reaches until the end of the document to make it apply to the last position as well.

For even finer control, you could also use the "beforeChange" event to filter out changes that you’d prefer not to allow.

I think this use case is sufficiently supported by the current API, and I don’t see a need to add more specific support to the core library.