LanguageOperations.java

package app.service;

import java.util.Collection;
import java.util.Collections;
import java.util.regex.Matcher;

import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyleSpansBuilder;

import app.model.ProgrammingLanguage;

/**
 * Common static operations that can be executed on any class
 * that implements {@link app.model.ProgrammingLanguage ProgrammingLanguage}
 */
public final class LanguageOperations {

  private LanguageOperations() {}
  
  /**
   * Use a matcher to find the styleclass of the next match
   * from several available styleclasses.
   * @param m The matcher
   * @param styleClasses Collection of styleclasses
   * @return The styleclass of the next match
   */
  private static String getMatch(Matcher m, Collection<String> styleClasses) {
    return styleClasses
      .stream()
      .filter(keyword -> m.group(keyword) != null)
      .findFirst()
      .orElse(""); // This is not possible, but is needed to convert Optional<String> to String
  }

  /**
   * Calculate syntax highlighting data for a code area
   * @param text The text to highlight
   * @param language The language object to use for highlighting
   * @return The syntax highlighting data
   * @see app.controllers.EditorController#setHighlighting(StyleSpans) EditorController.setHighlighting()
   */
  public static StyleSpans<Collection<String>> 
    syntaxHighlight(String text, ProgrammingLanguage language) {
    Matcher matcher = language.getPattern().matcher(text);
    StyleSpansBuilder<Collection<String>> spansBuilder = new StyleSpansBuilder<>();

    int lastKwEnd = 0;
    while(matcher.find()) {
      String styleClass = getMatch(matcher, language.getPatternMap().values());

      spansBuilder.add(
        Collections.emptyList(),
        matcher.start() - lastKwEnd
      );
      spansBuilder.add(
        Collections.singleton(styleClass),
        matcher.end() - matcher.start()
      );

      lastKwEnd = matcher.end();
    }

    spansBuilder.add(
      Collections.emptyList(),
      text.length() - lastKwEnd
    );

    return spansBuilder.create();
  }

}