Commit 718dc183 authored by Alexander Weigl's avatar Alexander Weigl
Browse files

rework on script area

parent ea456fa2
Pipeline #11303 passed with stage
in 2 minutes and 21 seconds
......@@ -166,14 +166,11 @@ public class DebuggerMainWindowController implements Initializable {
inspectionViewTabPane.createNewInspectionViewTab(model, true);
tabPane.getActiveScriptAreaTab().getScriptArea().getMarkedLines().addListener(new SetChangeListener<Integer>() {
@Override
public void onChanged(Change<? extends Integer> change) {
blocker.getBreakpoints().clear();
blocker.getBreakpoints().addAll(change.getSet());
}
});
//TODO this does not work any more
/*tabPane.getActiveScriptAreaTab().getScriptArea().getMarkedLines().addListener((SetChangeListener<Integer>) change -> {
blocker.getBreakpoints().clear();
blocker.getBreakpoints().addAll(change.getSet());
});*/
blocker.currentGoalsProperty().addListener((o, old, fresh) -> {
model.currentGoalNodesProperty().setAll(fresh);
......@@ -199,9 +196,7 @@ public class DebuggerMainWindowController implements Initializable {
int line = lm.getLine(scriptArea.getCaretPosition());
int inLine = lm.getCharInLine(scriptArea.getCaretPosition());
*/
ib.ignoreLinesUntil(tabPane.getActiveScriptAreaTab().getScriptArea().getCaretPosition());
ib.ignoreLinesUntil(tabPane.getSelectedScriptArea().getCaretPosition());
executeScript(ib, true);
}
......@@ -222,7 +217,7 @@ public class DebuggerMainWindowController implements Initializable {
blocker.deinstall();
statusBar.publishMessage("Parse ...");
try {
List<ProofScript> scripts = Facade.getAST(tabPane.getActiveScriptAreaTab().getScriptArea().getText());
List<ProofScript> scripts = Facade.getAST(tabPane.getSelectedScriptArea().getText());
statusBar.publishMessage("Creating new Interpreter instance ...");
ib.setScripts(scripts);
ib.captureHistory();
......@@ -235,7 +230,7 @@ public class DebuggerMainWindowController implements Initializable {
blocker.install(currentInterpreter);
System.out.println("Start of Script in: " + scripts.get(0).getStartPosition());
tabPane.getActiveScriptAreaTab().getScriptArea().setDebugMark(scripts.get(0).getStartPosition().getLineNumber());
tabPane.getSelectedScriptArea().setDebugMark(scripts.get(0).getStartPosition().getLineNumber());
interpreterService.interpreter.set(currentInterpreter);
interpreterService.mainScript.set(scripts.get(0));
......@@ -271,9 +266,9 @@ public class DebuggerMainWindowController implements Initializable {
private void saveScript(File scriptFile) {
try {
FileUtils.write(scriptFile, tabPane.getActiveScriptAreaTab().getScriptArea().getText(), Charset.defaultCharset());
FileUtils.write(scriptFile, tabPane.getSelectedScriptArea().getText(), Charset.defaultCharset());
} catch (IOException e) {
showExceptionDialog("Could not save file", "blubb", "...fsfsfsf fsa", e);
showExceptionDialog("Could not save sourceName", "blubb", "...fsfsfsf fsa", e);
}
}
......@@ -289,19 +284,18 @@ public class DebuggerMainWindowController implements Initializable {
assert scriptFile != null;
try {
String code = FileUtils.readFileToString(scriptFile, Charset.defaultCharset());
tabPane.createNewTab(scriptFile.getAbsolutePath());
openScript(code, tabPane.getActiveScriptAreaTab().getScriptArea());
ScriptArea area = tabPane.createNewTab(scriptFile);
openScript(code, area);
model.setScriptFile(scriptFile);
} catch (IOException e) {
showExceptionDialog("Exception occured", "",
"Could not load file " + scriptFile, e);
"Could not load sourceName " + scriptFile, e);
}
}
private void openScript(String code, ScriptArea area) {
model.setScriptFile(null);
if (area.textProperty().getValue() != "") {
System.out.println(area.textProperty().getValue());
if (!area.textProperty().getValue().isEmpty()) {
area.deleteText(0, area.textProperty().getValue().length());
}
area.setText(code);
......@@ -315,13 +309,13 @@ public class DebuggerMainWindowController implements Initializable {
if (keyFile != null) {
Task<Void> task = facade.loadKeyFileTask(keyFile);
task.setOnSucceeded(event -> {
statusBar.publishMessage("Loaded key file: %s", keyFile);
statusBar.publishMessage("Loaded key sourceName: %s", keyFile);
model.getCurrentGoalNodes().setAll(facade.getPseudoGoals());
});
task.setOnFailed(event -> {
event.getSource().exceptionProperty().get();
showExceptionDialog("Could not load file", "Key file loading error", "",
showExceptionDialog("Could not load sourceName", "Key sourceName loading error", "",
(Throwable) event.getSource().exceptionProperty().get()
);
});
......@@ -353,12 +347,12 @@ public class DebuggerMainWindowController implements Initializable {
*
* @param title of the dialog
* @param description of the files
* @param fileEndings file that should be shown
* @param fileEndings sourceName that should be shown
* @return
*/
private File openFileChooserSaveDialog(String title, String description, String... fileEndings) {
FileChooser fileChooser = getFileChooser(title, description, fileEndings);
// File file = fileChooser.showSaveDialog(inspectionViewTabPane.getInspectionViewTab().getGoalView().getScene().getWindow());
// File sourceName = fileChooser.showSaveDialog(inspectionViewTabPane.getInspectionViewTab().getGoalView().getScene().getWindow());
File file = fileChooser.showOpenDialog(statusBar.getScene().getWindow());
if (file != null) initialDirectory = file.getParentFile();
return file;
......@@ -366,7 +360,7 @@ public class DebuggerMainWindowController implements Initializable {
private File openFileChooserOpenDialog(String title, String description, String... fileEndings) {
FileChooser fileChooser = getFileChooser(title, description, fileEndings);
//File file = fileChooser.showOpenDialog(inspectionViewTabPane.getInspectionViewTab().getGoalView().getScene().getWindow());
//File sourceName = fileChooser.showOpenDialog(inspectionViewTabPane.getInspectionViewTab().getGoalView().getScene().getWindow());
File file = fileChooser.showOpenDialog(statusBar.getScene().getWindow());
if (file != null) initialDirectory = file.getParentFile();
return file;
......
......@@ -75,7 +75,7 @@ public class InspectionViewTab extends Tab {
setClosable(true);
}
});
mode.set(Mode.POSTMORTEM);
showCode.addListener(o -> {
if (showCode.get())
......
......@@ -2,23 +2,25 @@ package edu.kit.formal.gui.controls;
import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIcon;
import de.jensd.fx.glyphs.materialdesignicons.MaterialDesignIconView;
import edu.kit.formal.gui.model.Breakpoint;
import edu.kit.formal.gui.model.ConditionalBreakpoint;
import edu.kit.formal.gui.model.MainScriptIdentifier;
import edu.kit.formal.proofscriptparser.Facade;
import edu.kit.formal.proofscriptparser.ScriptLanguageLexer;
import edu.kit.formal.proofscriptparser.ast.ProofScript;
import edu.kit.formal.proofscriptparser.lint.LintProblem;
import edu.kit.formal.proofscriptparser.lint.LinterStrategy;
import javafx.beans.property.ListProperty;
import javafx.beans.property.SimpleListProperty;
import javafx.beans.property.SimpleObjectProperty;
import javafx.beans.Observable;
import javafx.beans.property.*;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.collections.ObservableSet;
import javafx.event.ActionEvent;
import javafx.geometry.Insets;
import javafx.geometry.Point2D;
import javafx.scene.Node;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.input.MouseButton;
import javafx.scene.input.MouseEvent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
......@@ -28,6 +30,7 @@ import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.text.Font;
import javafx.scene.text.FontPosture;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.CharStreams;
import org.antlr.v4.runtime.Token;
import org.fxmisc.richtext.CharacterHit;
......@@ -35,39 +38,46 @@ import org.fxmisc.richtext.CodeArea;
import org.fxmisc.richtext.MouseOverTextEvent;
import org.fxmisc.richtext.model.NavigationActions;
import org.fxmisc.richtext.model.Paragraph;
import org.fxmisc.richtext.model.StyleSpans;
import org.fxmisc.richtext.model.StyledText;
import org.reactfx.collection.LiveList;
import org.reactfx.value.Val;
import java.io.File;
import java.time.Duration;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Optional;
import java.util.function.IntFunction;
import java.util.stream.Collectors;
/**
* ScriptArea is the textarea on the left side of the GUI.
* It displays the script code and allows highlighting of lines and setting of breakpoints
*
*/
public class ScriptArea extends CodeArea {
private final ObjectProperty<File> filePath = new SimpleObjectProperty<>();
/**
* Lines to highlight?
*/
private ObservableSet<Integer> markedLines = FXCollections.observableSet();
private final SetProperty<Integer> markedLines =
new SimpleSetProperty<>(FXCollections.observableSet());
/**
* set by {@link ScriptTabPane}
*/
private final ObjectProperty<MainScriptIdentifier> mainScript = new SimpleObjectProperty<>();
private GutterFactory gutter;
private ANTLR4LexerHighlighter highlighter;
private ListProperty<LintProblem> problems = new SimpleListProperty<>(FXCollections.observableArrayList());
private SimpleObjectProperty<CharacterHit> currentMouseOver = new SimpleObjectProperty<>();
public ScriptArea() {
init();
}
public void setCurrentMouseOver(CharacterHit i) {
currentMouseOver.set(i);
}
private void init() {
this.setWrapText(true);
gutter = new GutterFactory();
......@@ -79,9 +89,12 @@ public class ScriptArea extends CodeArea {
textProperty().addListener((prop, oldValue, newValue) -> {
if (newValue.length() != 0) {
clearStyle(0, newValue.length());
setStyleSpans(0, highlighter.highlight(newValue));
StyleSpans<? extends Collection<String>> spans = highlighter.highlight(newValue);
if (spans != null) setStyleSpans(0, spans);
}
highlightProblems();
updateMainScriptMarker();
});
/* .successionEnds(Duration.ofMillis(100))
.hook(collectionRichTextChange -> this.getUndoManager().mark())
......@@ -105,17 +118,35 @@ public class ScriptArea extends CodeArea {
//System.out.println("characterPosition = " + characterPosition);
// move the caret to that character's position
this.moveTo(characterPosition, NavigationActions.SelectionPolicy.CLEAR);
});
ScriptAreaContextMenu cm = new ScriptAreaContextMenu(currentMouseOver);
this.setContextMenu(cm);
mainScript.addListener((observable) ->
updateMainScriptMarker());
setContextMenu(new ScriptAreaContextMenu());
}
private void updateMainScriptMarker() {
try {
MainScriptIdentifier ms = mainScript.get();
System.out.println("ScriptArea.updateMainScriptMarker");
if (ms != null && filePath.get().getAbsolutePath().equals(ms.getSourceName())) {
System.out.println(ms);
CharStream stream = CharStreams.fromString(getText(), filePath.get().getAbsolutePath());
Optional<ProofScript> ps = ms.find(Facade.getAST(stream));
if (ps.isPresent()) {
setMainMarker(ps.get().getStartPosition().getLineNumber());
return;
}
}
setMainMarker(-1);
} catch (NullPointerException e) {
}
}
private void installPopup() {
javafx.stage.Popup popup = new javafx.stage.Popup();
setMouseOverTextDelay(Duration.ofSeconds(1));
addEventHandler(MouseOverTextEvent.MOUSE_OVER_TEXT_BEGIN, e -> {
int chIdx = e.getCharacterIndex();
......@@ -147,14 +178,6 @@ public class ScriptArea extends CodeArea {
return box;
}
public ObservableSet<Integer> getMarkedLines() {
return markedLines;
}
public void setMarkedLines(ObservableSet<Integer> markedLines) {
this.markedLines = markedLines;
}
private void highlightProblems() {
LinterStrategy ls = LinterStrategy.getDefaultLinter();
try {
......@@ -170,24 +193,30 @@ public class ScriptArea extends CodeArea {
}
}
public MainScriptIdentifier getMainScript() {
return mainScript.get();
}
public void setMainScript(MainScriptIdentifier mainScript) {
this.mainScript.set(mainScript);
}
public ObjectProperty<MainScriptIdentifier> mainScriptProperty() {
return mainScript;
}
public void setText(String text) {
insertText(0, text);
}
private void onLineClicked(int idx) {
if (markedLines.contains(idx)) {
markedLines.remove(idx);
gutter.lineAnnotations.get(idx).set(null);
} else {
markedLines.add(idx);
gutter.lineAnnotations.get(idx).set(
new MaterialDesignIconView(MaterialDesignIcon.STOP_CIRCLE_OUTLINE)
);
}
private void toggleBreakpoint(int idx) {
GutterAnnotation a = gutter.getLineAnnotation(idx);
a.setBreakpoint(!a.isBreakpoint());
}
/**
* Highlight line given by the characterindex
*
* @param lineNumber
*/
public void highlightStmt(int lineNumber, String cssStyleTag) {
......@@ -228,6 +257,128 @@ public class ScriptArea extends CodeArea {
removeHighlightStmt(lineNumberOfSigMainScript - 1);
}
public File getFilePath() {
return filePath.get();
}
public void setFilePath(File filePath) {
this.filePath.set(filePath);
}
public ObjectProperty<File> filePathProperty() {
return filePath;
}
public void setMainMarker(int lineNumber) {
gutter.lineAnnotations.forEach(a -> a.setMainScript(false));
if (lineNumber > 0)
gutter.getLineAnnotation(lineNumber - 1).setMainScript(true);
}
public Collection<? extends Breakpoint> getBreakpoints() {
return gutter.lineAnnotations.stream()
.filter(GutterAnnotation::isBreakpoint)
.map(ga -> new Breakpoint(filePath.get(),
Integer.parseInt(ga.lineNumber.getText())))
.collect(Collectors.toList());
}
private static class GutterAnnotation extends HBox {
private MaterialDesignIconView iconMainScript = new MaterialDesignIconView(
MaterialDesignIcon.SQUARE_INC, "12"
);
private MaterialDesignIconView iconBreakPoint = new MaterialDesignIconView(
MaterialDesignIcon.STOP_CIRCLE_OUTLINE, "12"
);
private MaterialDesignIconView iconConditionalBreakPoint = new MaterialDesignIconView(
MaterialDesignIcon.ACCOUNT_CIRCLE, "12"
);
private StringProperty text;
private BooleanProperty breakpoint = new SimpleBooleanProperty();
private BooleanProperty mainScript = new SimpleBooleanProperty();
private SimpleObjectProperty<ConditionalBreakpoint> conditionalBreakpoint = new SimpleObjectProperty<>();
private Label lineNumber = new Label("not set");
public GutterAnnotation() {
text = lineNumber.textProperty();
breakpoint.addListener(this::update);
mainScript.addListener(this::update);
conditionalBreakpoint.addListener(this::update);
update(null);
}
public void update(Observable o) {
getChildren().setAll(lineNumber);
if (isMainScript()) getChildren().add(iconMainScript);
else addPlaceholder();
if (isBreakpoint() && conditionalBreakpoint.isNull().get())
getChildren().add(iconBreakPoint);
else if (isBreakpoint() && conditionalBreakpoint.isNotNull().get())
getChildren().add(iconConditionalBreakPoint);
else
addPlaceholder();
}
private void addPlaceholder() {
Label lbl = new Label();
lbl.setMinWidth(12);
lbl.setMinHeight(12);
getChildren().add(lbl);
}
public String getText() {
return text.get();
}
public void setText(String text) {
this.text.set(text);
}
public StringProperty textProperty() {
return text;
}
public boolean isBreakpoint() {
return breakpoint.get();
}
public void setBreakpoint(boolean breakpoint) {
this.breakpoint.set(breakpoint);
}
public BooleanProperty breakpointProperty() {
return breakpoint;
}
public boolean isMainScript() {
return mainScript.get();
}
public void setMainScript(boolean mainScript) {
this.mainScript.set(mainScript);
}
public BooleanProperty mainScriptProperty() {
return mainScript;
}
public ConditionalBreakpoint getConditionalBreakpoint() {
return conditionalBreakpoint.get();
}
public void setConditionalBreakpoint(ConditionalBreakpoint conditionalBreakpoint) {
this.conditionalBreakpoint.set(conditionalBreakpoint);
}
public SimpleObjectProperty<ConditionalBreakpoint> conditionalBreakpointProperty() {
return conditionalBreakpoint;
}
}
public class GutterFactory implements IntFunction<Node> {
private final Background defaultBackground =
......@@ -236,14 +387,15 @@ public class ScriptArea extends CodeArea {
private Insets defaultInsets = new Insets(0.0, 5.0, 0.0, 5.0);
private Paint defaultTextFill = Color.web("#666");
private Font defaultFont = Font.font("monospace", FontPosture.ITALIC, 13);
private ObservableList<SimpleObjectProperty<Node>> lineAnnotations = new SimpleListProperty<>(FXCollections.observableArrayList());
private ObservableList<GutterAnnotation> lineAnnotations =
new SimpleListProperty<>(FXCollections.observableArrayList());
public GutterFactory() {
nParagraphs = LiveList.sizeOf(getParagraphs());
for (int i = 0; i < 100; i++) {
lineAnnotations.add(new SimpleObjectProperty<>());
lineAnnotations.add(new GutterAnnotation());
}
// Update the gutter.
......@@ -266,38 +418,21 @@ public class ScriptArea extends CodeArea {
@Override
public Node apply(int idx) {
Val<String> formatted = nParagraphs.map(n -> format(idx + 1, n));
Label lineNo = new Label();
// bind label's text to a Val that stops observing area's paragraphs
// when lineNo is removed from scene
lineNo.textProperty().bind(formatted.conditionOnShowing(lineNo));
if (lineAnnotations.size() <= idx) {
for (int i = lineAnnotations.size(); i < idx + 1; i++) {
lineAnnotations.add(new SimpleObjectProperty<>());
}
}
Label labelInfo = new Label();
labelInfo.setMinHeight(12);
labelInfo.setMinWidth(12);
SimpleObjectProperty<Node> annotation = lineAnnotations.get(idx);
annotation.addListener((a, b, c) -> {
labelInfo.setGraphic(c);
GutterAnnotation hbox = getLineAnnotation(idx);
hbox.textProperty().bind(formatted);
hbox.setOnMouseClicked((mevent) -> {
if (mevent.getButton() == MouseButton.SECONDARY)
updateMainScriptMarker();
else
toggleBreakpoint(idx);
});
labelInfo.setGraphic(annotation.get());
HBox hbox = new HBox(lineNo, labelInfo);
hbox.setOnMouseClicked((mevent) -> onLineClicked(idx));
lineNo.setFont(defaultFont);
labelInfo.setFont(defaultFont);
hbox.lineNumber.setFont(defaultFont);
hbox.setBackground(defaultBackground);
lineNo.setTextFill(defaultTextFill);
hbox.lineNumber.setTextFill(defaultTextFill);
hbox.setPadding(defaultInsets);
hbox.getStyleClass().add("lineno");
hbox.setStyle("-fx-cursor: hand");
return hbox;
......@@ -308,35 +443,42 @@ public class ScriptArea extends CodeArea {
return String.format("%" + digits + "d", x);
}
}
private class ScriptAreaContextMenu extends ContextMenu {
MenuItem showPostmortemTab = new MenuItem("Show this State");
//MenuItem showPostmortemTab = new MenuItem("Show this State");
SimpleObjectProperty<CharacterHit> currentIdx = new SimpleObjectProperty<>();
public ScriptAreaContextMenu(SimpleObjectProperty<CharacterHit> currentMouseOver) {
super();
this.addEventHandler(ActionEvent.ACTION, e -> {
ScriptAreaContextMenu cm = (ScriptAreaContextMenu) e.getSource();
ScriptArea area = (ScriptArea) cm.getOwnerNode();
int chrIdx = currentIdx.get().getCharacterIndex().orElse(0);
if (chrIdx != 0) {
int lineNumber = area.offsetToPosition(chrIdx, Bias.Forward).getMajor();
area.highlightStmt(lineNumber, "line-highlight-postmortem");
public GutterAnnotation getLineAnnotation(int idx) {
if (lineAnnotations.size() <= idx) {
for (int i = lineAnnotations.size(); i < idx + 1; i++) {
lineAnnotations.add(new GutterAnnotation());
}
});
currentIdx.bind(currentMouseOver);
showPostmortemTab.addEventHandler(ActionEvent.ACTION, e -> {
System.out.println(currentIdx.get());
});
this.getItems().add(showPostmortemTab);
}
return lineAnnotations.get(idx);
}
}
public class ScriptAreaContextMenu extends ContextMenu {
public ScriptAreaContextMenu() {
Utils.createWithFXML(this);
}
public void setMainScript(ActionEvent event) {
System.out.println("ScriptAreaContextMenu.setMainScript");
List<ProofScript> ast = Facade.getAST(getText());
int pos = currentMouseOver.get().getInsertionIndex();
ast.stream().filter(ps ->
ps.getRuleContext().getStart().getStartIndex() <= pos
&& pos <= ps.getRuleContext().getStop().getStopIndex())
.findFirst()
.ifPresent(proofScript -> mainScript.set(
new MainScriptIdentifier(filePath.get().getAbsolutePath(),
proofScript.getStartPosition().getLineNumber(),
proofScript.getName(), ScriptArea.this)));
}