Replace decoration replaces the range in document when line moves up

As the title says, I have some replace decoration which replaces their respective ranges when the \n is deleted to move the line up. The expected result is that the replace decoration remain with their respective ranges remain but hidden because of the replace decoration.

For a example of the issue, I don’t have a very simple, hard coded example as the text is dynamic, so heres a code where there is a text abc, whose by default b is hidden through a replace decoration, b is only shown the selection/cursor froms next char is b.

// Remove b from abc in text using replace deco
function replacebfromabc(view: EditorView) {
  if (view.state.selection.ranges == undefined) return Decoration.set([]);
  
  let remove1 = []
  if (view.state.doc.sliceString(0).search('abc') != -1){ // if 'abc' is not available
    const from = view.state.doc.sliceString(0).search('abc')
    remove1.push(Decoration.replace({}).range(from+1, from+2)) // replace 'b' from 'abc' anywhere in the text
  }
  
  if (remove1 != []){
    return Decoration.set(remove1, true);
  }
  else if (remove1 == []) { 
    return Decoration.set([])
  }
}

// Add b from abc in text by removing replace deco when cursor froms next char is 'b'
function addbwhencursorisbeforeb(view:EditorView, decorations:DecorationSet) {
  if (view.state.selection.ranges == undefined) return decorations;
  if (view.state.sliceDoc(view.state.selection.ranges[0].from, view.state.selection.ranges[0].from+1) == 'b') { // If the cursor/selection from next char is 'b'
    decorations = decorations.update({
      filter: (f, t, v) => {
        return false // Remove any and all decorations.
      },
    });
  }
  return decorations;
}

// Plugin / Extention
export const Plugin = ViewPlugin.fromClass(
  class {
    decorations: DecorationSet;
    
    constructor(view:EditorView) {
      this.decorations = replacebfromabc(view) as DecorationSet;
    }

    update(update:ViewUpdate) {
      if (update.docChanged || update.viewportChanged || update.selectionSet) {
        console.log(update.view.state.doc.toJSON()); // to see text in console
        this.decorations = replacebfromabc(update.view) as DecorationSet;
        this.decorations = addbwhencursorisbeforeb(update.view, this.decorations);
      }
    }
  },
  {
    decorations: (v) => v.decorations,
  }
);

What happens is that b gets replace, when I add \n in front of abc and then remove it. Heres a demo:
Screen-Recording-2022-05-06-at-11.06.29-AM

A screen shot of the console(logging document text: console.log(update.view.state.doc.toJSON());):
Screenshot 2022-05-06 at 11.09.04 AM

I would like to keep the ranges with there replace decoration when I remove the \n before the line where the replace decoration is located, how may I go about it?

Thanks.

Edit: typo.

I don’t understand the issue. There’s a few very dodgy things in your code, but they may not be related (x == [] will always be false, arrays are compared by identity, and view.state.selection.ranges == undefined will also always be false).

What I did was use your plugin in an editor with content abc, add a newline before the text, and then delete it again. Nothing strange happened. Can you describe your reproduction steps more concretely?

I was trying to do something else, and I copied code from my older code where some of the these mattered, but here it doesn’t really matter.

This is the way to reproduce, the problem is that b gets replaced in doc, you should see this result in the console, and if you put your cursor right of a you don’t see b after entering and deleting the line, while you do see b before when putting the cursor in right of a before entering and deleting the line. Are you not getting the same result?

Screenshot 2022-05-06 at 11.09.04 AM

Thanks.

Nope. Which browser are you using?

Ah, I see, I am using macOS Monterey Version 12.1 (21C52):

  • Edge Version 98.0.1108.43 (Official build) (x86_64),
  • I also installed latest chromium just to check, as I haven’t updated Edge for a while, and found the same result: Chromium Version 103.0.5047.0 (Developer Build) (x86_64)

I also tried mobile browsers on my phone, which is a Samsung phone, with pretty much every major browser and found the default Internet browser which Samsung provides, Chrome, Edge has the same broken result.

The only browser which worked without the issue was my Firefox, both on my Macbook, and on my phone, so it seems like a problem with Chromium based browsers.

Where you able to replicate the issue?

Not on Chrome Linux. I’ll take a look on macOS when I have my MacBook near me.

1 Like

I still can’t get this to happen, even on macOS. Could you describe the document you start with and the steps you take in more detail? Or provide a full script that helps reproduce this?

I used Edge, and Chromium(latest) to test the issue on my Macbook Pro Intel, and the default document to start with is abc, where b is hidden and only shown when the cursor goes next to a. There are also console logs for the document in json which tells the actual document without any replace decoration.

To reproduce I have created this sandboxcode instance where you can see the code in action: Replace Decoration replacing ranges bug - CodeSandbox

Heres a demo of what I see using Chromium:

Thanks.

Okay, I see why I wasn’t able to reproduce this before — my setup had the default keymap included, which prevents this from happening. Make sure you bind Enter and Backspace to something reasonable, and the issue will go away.

Thanks a LOT! This was the issue that also plagued another issue caused with replace decoration, for which I replicated the code from @codemirror/commands and managed a hot-fix, I assumed that the standard keymap is imported by default, but that wasn’t the case, which I am learning as of now: Moving of cursor with different size mark decoration and replace decoration issues - /next - discuss.CodeMirror

So don’t mind me asking, but why isn’t standardKeymap not used by default, or am I missing something? Does the internals use different key bindings for moving the cursor, which isn’t imported from @codemirror/commands.

Again thanks a lot!

Because the library doesn’t force you to use a specific set of keybindings.

2 Likes