Hot to use dynamic regexp in MatchDecorator?

I have a variable outside CM that holds the regexp which I need to use for highlighting some parts of the text. But when I update this variable, the MatchDecorator keeps using the initial value.

I have read this question but still can’t understand how to fix it.

What is the proper way to dynamically update the reqexp used by MatchDecorator?

let currentRegex = /^(https?):/g;
let regexpComp = new Compartment

function updateCurrentRegexp(regexp) {
  currentRegex = regexp
  addressLineCm.dispatch({
    effects: regexpComp.reconfigure(syntaxTokens)
  })
}

const syntaxMatcher = new MatchDecorator({
  regexp: currentRegex,
  decorate: (add, from, to, match, view) => {
    console.log(from + " / " + to + " / " + currentRegex) // Same "from" and "to" all the time
    let tokenClass = "highlight"
    add(from, to, Decoration.mark({class: tokenClass}))
  }
})

const syntaxTokens = ViewPlugin.fromClass(class {
  constructor(view) {
    this.placeholders = syntaxMatcher.createDeco(view);
  }
  update(update) {
    this.placeholders = syntaxMatcher.createDeco(update.view);
  }},{
    decorations: (instance) => {
       return instance.placeholders;
    },
    provide: (plugin) =>
      EditorView.atomicRanges.of((view) => {
        return view.plugin(plugin)?.placeholders || Decoration.none;
      }),
  }
);

const editor = new EditorView({
  doc:  `https://mysite.com/api/v3/pat/:petId?param=value&another=123`,
  extensions: [
    minimalSetup, 
    regexpComp.of(syntaxTokens),
  ],
  parent: document.querySelector("#address-line")
})

You can’t – you make a new match decorator, and set things up so that your plugin uses it to recreate its decorations.

Thank you for the reply. Could you please elaborate on “set things up”?
That’s how I understand it: I create a new copy of syntaxMatcher, then create another copy of syntaxTokens, remove the previous instance of syntaxTokens from the editor and plug in the new instance. Can you point out what should I use for updating extensions on the fly.

The config example goes into that. But for this thing it might be easier to set up a state effect that you can dispatch to reconfigure the highlighter and have your view plugin look for that in transactions.

I saw this document and I can’t get it work unfortunately. Please tell me what I’m doing wrong here:

let currentRegex = protocolRegex

function updateCurrentRegexp(regexp) {
  currentRegex = regexp
  addressLineCm.dispatch({
    // Remove the current instance of the plugin
    effects: StateEffect.reconfigure.of([syntaxTokens]),
  })
  // Create a new copy
  console.dir(syntaxTokens) // ViewPlugin.id: 27
  syntaxTokens = composeTokens(syntaxMatcher, 'call')
  console.dir(syntaxTokens) // ViewPlugin.id: 28
  addressLineCm.dispatch({
    // Inject the updated plugin 
    effects: StateEffect.appendConfig.of(syntaxTokens)
  })
}

// Protocol part
const syntaxMatcher = new MatchDecorator({
  regexp: currentRegex,
  decorate: (add, from, to, match, view) => {
    console.log(from + " / " + to + " / " + currentRegex) // Same "from" and "to" all the time
    let tokenClass = "highlight"
    add(from, to, Decoration.mark({class: tokenClass}))
  }
})

// Initial plugin composition
let syntaxTokens = composeTokens(syntaxMatcher, 'call')

addressLineCm = new EditorView({
  doc:  `https://mysite.com/api/v3/pat/:petId?param=value&another=123`,
  extensions: [
    minimalSetup, 
    syntaxTokens
  ],
  parent: document.querySelector("#address-line")
})

Ok I get it work. To force your MatchDecorator to update reqexp on the fly you need (follow the comments):

let currentRegex = /^(https?):/g;
let regexpComp = new Compartment // 1. Wrap your decoration plugin with a Compartment. See the EditorView composition 

function updateCurrentRegexp(regexp) {
  currentRegex = regexp
  addressLineCm.dispatch({
    effects: regexpComp.reconfigure(syntaxTokens) // 2. When your reqexp outside CM gets changed, tell the Compartment to reconfigure the decoration plugin
  })
}

function composeSyntaxMatcher() { // 3. Wrap the matcher into a function. It'll called each time when Compartment reconfigures the decoration plugin
  return new MatchDecorator({
    regexp: currentRegex,
    decorate: (add, from, to, match, view) => {
      let tokenClass = "highlight"
      add(from, to, Decoration.mark({class: tokenClass}))
    }
  })
}

let syntaxTokens = ViewPlugin.fromClass(class {
  constructor(view) {
    let matcher = composeSyntaxMatcher() // 4. Here you create a matcher for the first time
    this.placeholders = matcher.createDeco(view);
  }
  update(update) { // 5. This code gets executed when the Compartment reconfigures this plugin
    let matcher = composeSyntaxMatcher() // 5.1 Here you re-create the matcher and it uses the updated reqexp
    this.placeholders = matcher.createDeco(update.view);
  }
}, {
  decorations: (instance) => {
     return instance.placeholders;
  },
  provide: (plugin) =>
    EditorView.atomicRanges.of((view) => {
      return view.plugin(plugin)?.placeholders || Decoration.none;
    }),
});

addressLineCm = new EditorView({
  doc:  `https://mysite.com/api/v3/pat/:petId?param=value&another=123`,
  extensions: [
    regexpComp.of(syntaxTokens) // 1. Here you wrap decoration plugin with the Compartment 
  ],
  parent: document.querySelector("#address-line")
})