Add syntax highlighting framework
This commit is contained in:
parent
f94612cf87
commit
1860d4c57f
@ -1,17 +1,20 @@
|
|||||||
package app.controllers;
|
package app.controllers;
|
||||||
|
|
||||||
import java.net.URL;
|
import java.net.URL;
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.ResourceBundle;
|
import java.util.ResourceBundle;
|
||||||
|
|
||||||
import com.google.common.eventbus.EventBus;
|
import com.google.common.eventbus.EventBus;
|
||||||
|
|
||||||
import org.fxmisc.richtext.CodeArea;
|
import org.fxmisc.richtext.CodeArea;
|
||||||
import org.fxmisc.richtext.LineNumberFactory;
|
import org.fxmisc.richtext.LineNumberFactory;
|
||||||
|
import org.fxmisc.richtext.model.StyleSpans;
|
||||||
import org.fxmisc.richtext.model.TwoDimensional.Bias;
|
import org.fxmisc.richtext.model.TwoDimensional.Bias;
|
||||||
import org.fxmisc.richtext.model.TwoDimensional.Position;
|
import org.fxmisc.richtext.model.TwoDimensional.Position;
|
||||||
|
|
||||||
import app.events.EditorChangedEvent;
|
import app.events.EditorChangedEvent;
|
||||||
import app.model.Model;
|
import app.model.Model;
|
||||||
|
import app.service.LanguageOperations;
|
||||||
import javafx.fxml.FXML;
|
import javafx.fxml.FXML;
|
||||||
import javafx.fxml.Initializable;
|
import javafx.fxml.Initializable;
|
||||||
import javafx.scene.input.KeyEvent;
|
import javafx.scene.input.KeyEvent;
|
||||||
@ -43,6 +46,10 @@ public class EditorController implements Initializable, Controller {
|
|||||||
this.model = model;
|
this.model = model;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void setHighlighting(StyleSpans<Collection<String>> highlighting) {
|
||||||
|
this.editor.setStyleSpans(0, highlighting);
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles events from the editor, and reflects them in the model
|
* Handles events from the editor, and reflects them in the model
|
||||||
* @param event The object containing metadata of the event
|
* @param event The object containing metadata of the event
|
||||||
@ -57,6 +64,10 @@ public class EditorController implements Initializable, Controller {
|
|||||||
pos.getMajor() + 1,
|
pos.getMajor() + 1,
|
||||||
pos.getMinor()
|
pos.getMinor()
|
||||||
));
|
));
|
||||||
|
this.setHighlighting(
|
||||||
|
LanguageOperations.syntaxHighlight(
|
||||||
|
this.editor.getText(),
|
||||||
|
Model.getLanguage()));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
71
src/main/java/app/languages/Java.java
Normal file
71
src/main/java/app/languages/Java.java
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package app.languages;
|
||||||
|
|
||||||
|
import java.net.URL;
|
||||||
|
import java.util.AbstractMap;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.regex.Pattern;
|
||||||
|
import java.util.stream.Collectors;
|
||||||
|
|
||||||
|
import app.model.ProgrammingLanguage;
|
||||||
|
|
||||||
|
public class Java implements ProgrammingLanguage {
|
||||||
|
|
||||||
|
private String name = "Java";
|
||||||
|
private URL iconPath = this.getClass().getResource("");
|
||||||
|
|
||||||
|
private static final String[] keywords = new String[] {
|
||||||
|
"abstract", "assert", "boolean", "break", "byte",
|
||||||
|
"case", "catch", "char", "class", "const",
|
||||||
|
"continue", "default", "do", "double", "else",
|
||||||
|
"enum", "extends", "final", "finally", "float",
|
||||||
|
"for", "goto", "if", "implements", "import",
|
||||||
|
"instanceof", "int", "interface", "long", "native",
|
||||||
|
"new", "package", "private", "protected", "public",
|
||||||
|
"return", "short", "static", "strictfp", "super",
|
||||||
|
"switch", "synchronized", "throw", "throws",
|
||||||
|
"transient", "try", "void", "volatile", "while"
|
||||||
|
};
|
||||||
|
|
||||||
|
private static Entry<Pattern, String> e(String k, String v) {
|
||||||
|
return new AbstractMap.SimpleEntry<>(Pattern.compile(k), v);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static final Map<Pattern, String> pattern =
|
||||||
|
Map.ofEntries(
|
||||||
|
e("\\b(" + String.join("|", keywords) + ")\\b", "keyword"), // <public>, <static>, ....
|
||||||
|
e("this", "this"), // <this>
|
||||||
|
e("true", "true"), // <true>
|
||||||
|
e("false", "false"), // <false>
|
||||||
|
e("\\(|\\)", "paranthesis"), // <(*)>
|
||||||
|
e("\\{|\\}", "curlyBrackets"), // <{*}>
|
||||||
|
e("\\[|\\]", "squareBrackets"), // <[*]>
|
||||||
|
e(";", "semiColon"), // <;>
|
||||||
|
e("[A-Z]\\w+", "identifier"), // <ClassName> or <InterfaceName>
|
||||||
|
e("\\.\\w+", "property"), // <.property>
|
||||||
|
e("(?<=this\\.)?\\w+(?=\\(*\\))", "method") // <this.function(*)>
|
||||||
|
);
|
||||||
|
|
||||||
|
public String getName() {
|
||||||
|
return this.name;
|
||||||
|
}
|
||||||
|
|
||||||
|
public URL getIcon() {
|
||||||
|
return this.iconPath;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Map<Pattern, String> getPatternMap() {
|
||||||
|
return Java.pattern;
|
||||||
|
}
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/16517689/java-regex-capturing-groups-indexes/16582083
|
||||||
|
|
||||||
|
public Pattern getPattern() {
|
||||||
|
return Pattern.compile(
|
||||||
|
Java.pattern
|
||||||
|
.entrySet()
|
||||||
|
.stream()
|
||||||
|
.map(e -> String.format("(?<%s>%s)", e.getValue(), e.getKey()))
|
||||||
|
.collect(Collectors.joining("|")));
|
||||||
|
}
|
||||||
|
}
|
@ -1,7 +1,5 @@
|
|||||||
package app.model;
|
package app.model;
|
||||||
|
|
||||||
import app.languages.Java;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Data model of the application
|
* Data model of the application
|
||||||
*/
|
*/
|
||||||
|
51
src/main/java/app/service/LanguageOperations.java
Normal file
51
src/main/java/app/service/LanguageOperations.java
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
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;
|
||||||
|
|
||||||
|
public class LanguageOperations {
|
||||||
|
|
||||||
|
private static String getMatch(Matcher m, Collection<String> kws) {
|
||||||
|
return kws
|
||||||
|
.stream()
|
||||||
|
.filter(keyword -> m.group(keyword) != null)
|
||||||
|
.findFirst()
|
||||||
|
.orElse(""); // This is not possible, but is needed to convert Optional<String> to String
|
||||||
|
}
|
||||||
|
|
||||||
|
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();
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user