@codemirror/view 6.39.0-beta.1: Looking for testers

Hi all. In the past few weeks, I’ve been doing a bunch of work on the @codemirror/view code. The direct motivation for this was a new feature, but in the process of implementing that I realized that it was stretching the already gnarly view update code beyond its limit. Since I had been thinking about rewriting that system for a while, I went ahead and did the rewrite.

So that’s good, probably. The code really did get less scary, and I was able to implement my feature. But because, despite the project having a good test suite, so much changed code is likely to introduce at least some regressions, I want to give you all a heads-up. I’ve published the new code as a beta version (6.39.0-beta.1) and would love for people who have a good set of tests of their own to please install that and see if everything still works for them.

The feature that was added is an ability to define ‘block wrappers’, which are DOM elements that are rendered around one or more lines and block widgets in the editor. There’s a new facet EditorView.blockWrappers, which works similar to EditorView.decorations in that you feed it RangeSets holding BlockWrapper objects that indicate where the wrappers go. See the doc comments in the updated type declaration file for details.

This is the full set of code changes:

4 Likes

I actually should have said to install from the beta npm channel (npm i @codemirror/view@beta) rather than a specific version. There’s already a beta.2 published.

1 Like

Note for anyone trying this out: I ran into some issues installing the beta version of @codemirror/view because npm won’t resolve a subdependency to the top-level installed beta version. So by default you’ll end up with @codemirror/view@6.39.0-beta.3 at the top level, but still have @codemirror/view@38.8.8 underneath @codemirror/commands, @codemirror/autocomplete, etc. The way to resolve this is by adding an entry to your overrides field in package.json:

  "overrides": {
    "@codemirror/view": "$@codemirror/view"
  },

This will force all subdependencies to use the beta version installed at the top level.

However, there is a bug in recent versions of npm that prevents this from working as expected. It’s resolved, but the change hasn’t been released yet. There’s also another bug in older versions of npm that may cause issues as well. However, I’ve confirmed that things are working as expected in npm@11.1.0. So you may need to first upgrade/roll back npm to that version by running npm install –global npm@11.1.0, then finally run npm install in your project.

So far I’ve just installed the beta and ran the test suites in our project without any additional changes. Out of about 400 tests we have 9 tests failing.

Eight of those failures appear to be the same problem: mark decorations are splitting apart more than they should. We have many cases with nested decorations, and we ensure that the outer element is not split by increasing markSide as we go deeper in the tree. In the current release this works as expected. In the beta this appears to be working most of the time but occasionally (possibly when the change is in response to user input or paired with a document change?) the outer decoration is being incorrectly split apart on inner decoration boundaries. The element structure sometimes corrects itself after this happens.

The other failure is a new error being logged to the console during a test: update listener: [Error: No tile at position -1]. It looks like we have a code path where we are calling update.view.coordsAtPos(-1, 1) in this case. I think this points to a bug on our end (a position of -1 doesn’t make any sense) but it does also indicate a logging/error handling change that may or may not have been expected on your end.

Thanks, that’s very useful to know. Any chance you can reduce one of those cases to a self-contained example? (Or just describe the steps it takes.)

I wasn’t aware that installing a beta package was this broken. That’s really annoying. Thanks for posting an explanation of how you worked around it.

I’ve been trying to reproduce this issue and parse what you mean by this sentence, but I’m coming up blank. There’s no identifier markSide in the project’s API. Can you clarify what you mean by this?

I did catch one issue with a randomized test, which doesn’t entirely match your description (it would lead to pieces of old content being inappropriately left in the DOM) but may have been mistaken for it. Tagged the fix as beta.4.

Sorry, meant to say startSide rather than markSide. I installed beta-4 but the tests are still failing - I’ll see if I can get a minimal repro working

Thankfully I was able to get a nice minimal reproduction in CodeSandbox. The setup here is a simple case of an outer mark decoration around an inner decoration. For the demo, the outer decorations wrap words and the inner decorations highlight every other letter. I’ve styled the outer decoration with an outline to make it obvious when the outer decoration is incorrectly split apart.

On initial render the result is correct - none of the word decorations are split. But if you start typing into any word, the outer decoration is split apart at the first instance of the inner decoration. In this case I’ve typed ‘test’ into one of the words, and the outer decoration has split apart at the first letter:

Thanks for providing a test case! That made this easy to find. A new release 6.39.0-beta.5 fixes this issue.

Martin,

I’m having trouble with two keybindings when testing this update:

  1. Adding keymap.of([indentWithTab]) to the list of extensions fails with a TypeScript error shown below. Any ideas?
  2. I can’t seem to use the @codemirror/search default keybinding of Mod-Alt-g: gotoLine. Do I need to explicitly include this in my list of extensions?

Thanks for your work on this amazing library!

Bryan

src/CodeMirror-integration.mts:1069:32 - error TS2322: Type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.38.8/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).KeyBinding’ is not assignable to type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.39.0-beta.5/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).KeyBinding’.
Types of property ‘run’ are incompatible.
Type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.38.8/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).Command | undefined’ is not assignable to type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.39.0-beta.5/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).Command | undefined’.
Type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.38.8/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).Command’ is not assignable to type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.39.0-beta.5/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).Command’.
Types of parameters ‘target’ and ‘target’ are incompatible.
Type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.39.0-beta.5/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).EditorView’ is not assignable to type ‘import(“C:/Users/bj147/Documents/git/CodeChat_Editor/client/node_modules/.pnpm/@codemirror+view@6.38.8/node_modules/@codemirror/view/dist/index”, { with: { “resolution-mode”: “import” } }).EditorView’.
Types have separate declarations of a private property ‘dispatchTransactions’.

1069                     keymap.of([indentWithTab]),
~~~~~~~~~~~~~

It looks like I have a mix of the released @codemirror/view and the beta. I’m using pnpm; I’ve included overrides, but haven’t figured out how to fix this yet. Any guidance would be appreciated. I’m running pnpm v. 10.24.0.

@bjones I also ran into that TypeScript error when I had initially installed the beta and had two different versions installed at the same time. Looks like pnpm may require pnpm.overrides instead?

The error already shows the issue: there’s multiple versions of @codemirror/view installed, because apparently when you install a beta npm doesn’t deduplicate. The easiest way to test this locally might be to just install the beta off the side and then copy the content of its dist into your project’s node_modules/@codemirror/view/dist, rebuild, and run your tests.

Or wait a couple of days, and I’ll release this as a proper 6.38.0 version.

Thanks! That’s exactly what I was looking for – I’m testing now.

There’s now a proper 6.39.0 release with these changes.

2 Likes

I think I’m encountering a navigation bug from decorated lines with padding using arrow up or down, but works fine using arrow left or right. I tried with v6.38.8 and it’s working there. Here’s a reproduction.

That looks like it was already broken in 6.38, but should now be fixed in 6.39.2.

1 Like

I’m running into one more issue that’s blocking us from upgrading to this release. As best as I can tell, something about the mark decorations I am rendering is causing CodeMirror to throw an exception on the next user interaction:

dist-DXd2akvF.js?v=4b616e0b:2084 Uncaught TypeError: Cannot destructure property 'tile' of 'parents.pop(...)' as it is undefined.
    at TilePointer.advance (dist-DXd2akvF.js?v=4b616e0b:2084:6)
    at TileUpdate.forward (dist-DXd2akvF.js?v=4b616e0b:2539:12)
    at TileUpdate.run (dist-DXd2akvF.js?v=4b616e0b:2417:9)
    at dist-DXd2akvF.js?v=4b616e0b:2702:25
    at DOMObserver.ignore (dist-DXd2akvF.js?v=4b616e0b:6214:11)
    at DocView.updateInner (dist-DXd2akvF.js?v=4b616e0b:2698:12)
    at DocView.update (dist-DXd2akvF.js?v=4b616e0b:2690:9)
    at EditorView.update (dist-DXd2akvF.js?v=4b616e0b:6814:27)
    at EditorView.dispatchTransactions (dist-DXd2akvF.js?v=4b616e0b:6733:143)
    at EditorView.dispatch (dist-DXd2akvF.js?v=4b616e0b:6752:8)

I’m having a hard time coming up with a reproducible test case for this because the error is not thrown when actually rendering the decorations but instead on the next user interaction, which makes it hard to trace down the root cause.

I was however able to track down the line that is throwing to this statement in the TilePointer class. Looks like there is a case where parents can be empty. Perhaps the if (!dist && !parents.length) break guard before it should have an || instead of an && operator?