Chris W.
Chris W.

Reputation: 39219

How to use the Ace editor validator without instantiating an Ace editor instance?

I use react-ace to create a CSS text editor in my React app.

That looks something like...

import Ace from 'react-ace'

...
  <Ace 
    mode="css" 
    value={value} 
    onChange={onValueChange} 
    onValidate={onValidate} 
    ...
  />
...

This works fine and dandy—highlighting CSS syntax errors and warnings. Also, the onValidate returns the error/warning "annotations" data structure.

However there is a need, elsewhere in the application, to run the same validator used in this React Ace component, but outside of the context of this Component. Essentially I need to pass the content in value through the error/warning annotation system, but can't instantiate this react element.

I've tried the following:

import { EditSession } from 'brace'; # "brace" is the "module" compatible version of the ace editor that our "react-ace" uses
import 'brace/mode/css';

export const getCssAnnotations = (value)=> {
  const editSession = new EditSession(value);
  editSession.setMode('ace/mode/css');
  const annotations = editSession.getAnnotations();
  return annotations;
};

However, the annotations returned by this function are always []! I assume this is because I'm just accessing the annotation setter/getter interface, and not actually running the annotations creator. But I can't figure out what actually does the annotations work normally.

I've looked at docs on Creating a Syntax Highlighter for Ace, but don't understand if/why a web worker would need to be involved here.

Thanks!

Upvotes: 3

Views: 4139

Answers (1)

Michael Doye
Michael Doye

Reputation: 8171

This doesn't work, because editSession uses web worker to generate annotations which is async:

editSession.on('changeAnnotation', () => {
    let annotations = editSession.getAnnotations();
    callback(null, annotations)
});

docs

Note that currently each editSession creates a new worker, so it is better to use setValue on an existing instance of editSession, or call editSession.destroy() before calling the callback


So a full solution might look like:

const getAnnotationsPromise = (value, mode)=> {
  const editSession = new EditSession(value);
  editSession.setMode(`ace/mode/${mode}`);

  return new Promise((resolve)=> {
    editSession.on('changeAnnotation', () => {
      const annotations = editSession.getAnnotations();
      editSession.removeAllListeners('changeAnnotation');
      editSession.destroy();
      resolve(annotations);
    });
  });
};

Upvotes: 4

Related Questions