1
0
mirror of https://github.com/aNNiMON/HotaruFX.git synced 2024-09-19 14:14:21 +03:00

Add syntax highlighter

This commit is contained in:
Victor 2017-09-04 18:32:05 +03:00
parent 2fb70d1b80
commit 5acaa5ee88
3 changed files with 85 additions and 1 deletions

View File

@ -1,5 +1,6 @@
package com.annimon.hotarufx;
import com.annimon.hotarufx.ui.controller.EditorController;
import java.io.IOException;
import javafx.application.Application;
import javafx.fxml.FXMLLoader;
@ -9,12 +10,15 @@ import lombok.val;
public class Main extends Application {
private EditorController controller;
@Override
public void start(Stage primaryStage) {
primaryStage.setTitle("HotaruFX");
try {
val loader = new FXMLLoader(getClass().getResource("/fxml/Editor.fxml"));
val scene = new Scene(loader.load());
controller = loader.getController();
primaryStage.setScene(scene);
} catch (IOException e) {
// TODO: notice me!!
@ -22,6 +26,14 @@ public class Main extends Application {
primaryStage.show();
}
@Override
public void stop() throws Exception {
if (controller != null) {
controller.stop();
}
super.stop();
}
public static void main(String[] args) {
launch(args);
}

View File

@ -0,0 +1,63 @@
package com.annimon.hotarufx.ui;
import com.annimon.hotarufx.lexer.HotaruLexer;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.Optional;
import java.util.concurrent.ExecutorService;
import javafx.concurrent.Task;
import lombok.RequiredArgsConstructor;
import lombok.val;
import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.StyleSpans;
import org.fxmisc.richtext.StyleSpansBuilder;
@RequiredArgsConstructor
public class SyntaxHighlighter {
private final CodeArea editor;
private final ExecutorService executor;
public void init() {
editor.richChanges()
.filter(ch -> !ch.getInserted().equals(ch.getRemoved()))
.successionEnds(Duration.ofMillis(500))
.supplyTask(this::computeHighlightingAsync)
.awaitLatest(editor.richChanges())
.filterMap(t -> Optional.ofNullable(t.isSuccess() ? t.get() : null))
.subscribe(spans -> editor.setStyleSpans(0, spans));
}
public void release() {
executor.shutdown();
}
private Task<StyleSpans<Collection<String>>> computeHighlightingAsync() {
val text = editor.getText();
val task = new Task<StyleSpans<Collection<String>>>() {
@Override
protected StyleSpans<Collection<String>> call() throws Exception {
val spans = new StyleSpansBuilder<Collection<String>>();
for (val t : new HotaruLexer(text).tokenize()) {
val category = t.getType().getPrimaryCategory();
switch (category) {
case "string":
case "keyword":
case "comment":
case "number":
spans.add(Collections.singleton(category), t.getLength());
break;
default:
spans.add(Collections.emptyList(), t.getLength());
break;
}
}
return spans.create();
}
};
executor.execute(task);
return task;
}
}

View File

@ -9,13 +9,14 @@ import com.annimon.hotarufx.lib.Context;
import com.annimon.hotarufx.parser.HotaruParser;
import com.annimon.hotarufx.parser.ParseError;
import com.annimon.hotarufx.parser.visitors.InterpreterVisitor;
import com.annimon.hotarufx.ui.SyntaxHighlighter;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.time.Duration;
import java.util.Arrays;
import java.util.ResourceBundle;
import java.util.concurrent.Executors;
import javafx.application.Platform;
import javafx.event.ActionEvent;
import javafx.fxml.FXML;
@ -37,6 +38,8 @@ public class EditorController implements Initializable {
@FXML
private TitledPane logPane;
private SyntaxHighlighter syntaxHighlighter;
@FXML
private void handleMenuExit(ActionEvent event) {
// TODO: confirmation
@ -81,9 +84,15 @@ public class EditorController implements Initializable {
@Override
public void initialize(URL location, ResourceBundle resources) {
editor.setParagraphGraphicFactory(LineNumberFactory.get(editor));
syntaxHighlighter = new SyntaxHighlighter(editor, Executors.newSingleThreadExecutor());
syntaxHighlighter.init();
editor.replaceText(0, 0, readProgram("/main.hfx"));
}
public void stop() {
syntaxHighlighter.release();
}
private static String readProgram(String path) {
val fallbackProgram = "composition(640, 480, 25)";
try (InputStream is = Main.class.getResourceAsStream(path)) {