Adding and removing decoration failing as effect.is() is always false?

Hello,
I’m using the CodeMirror package to write a coding tutor, but I’m struggling to add and remove decorations. I want to highlight the text in specific places via a function, and if the code is updated (which means it was edited) the highlighting should disappear.

It worked as long as I only added a highlight, but after adding a StateEffect to remove the highlighting I added the condition to check for effect.is(), which will always return false for me.

I based this off of the example: CodeMirror Decoration Example
and this question: How to remove a decoration mark from state

My code looks like this:

const highlightExtension = StateField.define({
  create() {
    return Decoration.none
  },
  update(decorations, transaction) {
    decorations = decorations.map(transaction.changes)
    for (let effect of transaction.effects) {
      if (effect.is(addHightlight)) {
          decorations = decorations.update({ add: effect.value, sort: true })
      } else if (effect.is(addHightlight)) {
        decorations = decorations.update({
          filter: (f, t, value) => value.range !== undefined,
        })
      } else {
        // will always land here. -> Console Error: effect.is is not a function
      }
    }
    return decorations
  },
  provide: (f) => EditorView.decorations.from(f),
})
const addHightlight = StateEffect.define()
const removeHighlight = StateEffect.define()
const highlightDecoration = Decoration.mark({
  attributes: { style: "background-color: red" },
})

let state = EditorState.create({
  doc: initialcode,
  extensions: [basicSetup, javascript(), highlightExtension],
})

function addDecorationHighlight(start, end) {
  view.dispatch({
    effects: StateEffect.define({
      map: ({ from, to }) => addHighlight.of({ from, to }),
    }).of([highlightDecoration.range(start, end)]),
  })
}

function removeDecorationHighlight(start, end) {
  view.dispatch({
    effects: StateEffect.define({
      map: ({ from, to }) => removeHighlight.of({ from, to }),
    }).of([highlightDecoration.range(start, end)]),
  })
}

let view = new EditorView({
  state,
  parent: document.getElementById("codeeditor"),
})

I`m not sure what exactly is different in my code and why it doesn’t work. Appreciate any help and tips (or code snippets). Thank you very much!

1 Like

This never makes sense—you’re defining the effect right there, creating a single instance of it, and then throwing it away, rather than dispatching a named effect that you can check for with is.

2 Likes

Thank you! This felt repetitive, but didn’t know what to change.

I just tried the adjustment, which now looks like this for anyone else wondering:

function addDecorationHighlight(start, end) {
  view.dispatch({
    effects: addHighlight.of({ from: start, to: end}),
  })
}

function removeDecorationHighlight(start, end) {
  view.dispatch({
    effects: removeHighlight.of({ from: start, to: end }),
  })
}

Great work btw:)

1 Like

I was just doing something similar (adding and removing decorations), and I tried to understand your code.

It seems to me, that you would be removing all effects that don’t have a range field in their associated value. I am guessing it seems to work, because you don’t have other effects than the ones you’re adding, but it would not work in a more generic context.

Also, you are not using the value passed via removeHighlight.of(...), so you might as well do removeHighlight.of(null). This way, removeHighlight can remain just a token effect: const removeHighlight = StateEffect.define(); and not be defined like addHighlight (using the map: field of the conf).

What do you think?