Skip to content

Commit

Permalink
Feature: If possible save the last selected node in the proof, to res…
Browse files Browse the repository at this point in the history
…elect it upon loading (issue #3297).
  • Loading branch information
unp1 committed Nov 14, 2023
1 parent ac66700 commit 638e108
Show file tree
Hide file tree
Showing 24 changed files with 176 additions and 84 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1678,7 +1678,7 @@ protected void assertSaveAndReload(File baseDir, String javaPathInBaseDir,
KeYEnvironment<DefaultUserInterfaceControl> reloadedEnv = null;
SymbolicExecutionTreeBuilder reloadedBuilder = null;
try {
ProofSaver saver = new ProofSaver(env.getProof(), tempFile.getAbsolutePath(),
ProofSaver saver = new ProofSaver(env.getProof(), null, tempFile.getAbsolutePath(),
KeYConstants.INTERNAL_VERSION);
assertNull(saver.save());
// Load proof from saved *.proof file
Expand Down
4 changes: 2 additions & 2 deletions key.core/src/main/java/de/uka/ilkd/key/proof/Proof.java
Original file line number Diff line number Diff line change
Expand Up @@ -1344,7 +1344,7 @@ public void setProofFile(File proofFile) {
}

public void saveToFile(File file) throws IOException {
ProofSaver saver = new ProofSaver(this, file);
ProofSaver saver = new ProofSaver(this, null, file);
saver.save();
}

Expand All @@ -1356,7 +1356,7 @@ public void saveToFile(File file) throws IOException {
* @throws IOException on any I/O error
*/
public void saveProofObligationToFile(File file) throws IOException {
ProofSaver saver = new ProofSaver(this, file, false);
ProofSaver saver = new ProofSaver(this, null, file, false);
saver.save();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -740,15 +740,17 @@ private ReplayResult replayProof(Proof proof) {
replayResult =
replayer.replay(problemInitializer.getListener(), problemInitializer.getProgMon());

lastTouchedNode = replayResult.getLastSelectedGoal() != null
? replayResult.getLastSelectedGoal().node()
: proof.root();
lastTouchedNode =
replayResult.savedSelectedNode() != null ? replayResult.savedSelectedNode()
: replayResult.lastSelectedGoal() != null
? replayResult.lastSelectedGoal().node()
: proof.root();

} catch (Exception e) {
if (parserResult == null || parserResult.errors() == null
|| parserResult.errors().isEmpty() || replayer == null
|| replayResult == null || replayResult.getErrors() == null
|| replayResult.getErrors().isEmpty()) {
|| replayResult == null || replayResult.errors() == null
|| replayResult.errors().isEmpty()) {
// this exception was something unexpected
errors.add(e);
}
Expand All @@ -758,10 +760,10 @@ private ReplayResult replayProof(Proof proof) {
errors.addAll(parserResult.errors());

Check warning on line 760 in key.core/src/main/java/de/uka/ilkd/key/proof/io/AbstractProblemLoader.java

View workflow job for this annotation

GitHub Actions / qodana

Constant conditions & exceptions

Argument `parserResult.errors()` might be null
}
status += (status.isEmpty() ? "Proof replayed successfully." : "\n\n")
+ (replayResult != null ? replayResult.getStatus()
+ (replayResult != null ? replayResult.status()
: "Error while loading proof.");
if (replayResult != null) {
errors.addAll(replayResult.getErrors());
errors.addAll(replayResult.errors());
}

StrategyProperties newProps =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ private void save(final String filename, final Proof proof) {
// there may be concurrent changes to the proof... whatever
final Runnable r = () -> {
try {
new ProofSaver(proof, filename, KeYConstants.INTERNAL_VERSION).save();
new ProofSaver(proof, null, filename, KeYConstants.INTERNAL_VERSION).save();
LOGGER.info("File saved: {}", filename);
} catch (Exception e) {
LOGGER.error("Autosaving file {} failed.", filename, e);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import java.io.IOException;
import java.util.zip.GZIPOutputStream;

import de.uka.ilkd.key.proof.Node;
import de.uka.ilkd.key.proof.Proof;

/**
Expand All @@ -22,11 +23,13 @@ public class GZipProofSaver extends ProofSaver {
* Instantiates a new proof saver.
*
* @param proof the non-<code>null</code> proof to save
* @param selectedNode the Node selected at time of saving or <code>null</code> if no
* information available
* @param fileName the name of the file to write to
* @param internalVersion the internal version
*/
public GZipProofSaver(Proof proof, String fileName, String internalVersion) {
super(proof, fileName, internalVersion);
public GZipProofSaver(Proof proof, Node selectedNode, String fileName, String internalVersion) {
super(proof, selectedNode, fileName, internalVersion);
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ enum ProofElementID {
USER_INTERACTION("userinteraction"), PROOF_SCRIPT("proofscript"), NEW_NAMES("newnames"),
AUTOMODE_TIME("autoModeTime"), KeY_LOG("keyLog"), KeY_USER("keyUser"),
KeY_VERSION("keyVersion"), KeY_SETTINGS("keySettings"), OPEN_GOAL("opengoal"),
NOTES("notes"), SOLVERTYPE("solverType"), MODALITY("modality");
NOTES("notes"), SOLVERTYPE("solverType"), MODALITY("modality"), SELECTED_NODE("selected");

private final String rawName;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ public class IntermediatePresentationProofFileParser implements IProofFileParser
private BranchNodeIntermediate root = null; // the "dummy ID" branch
private NodeIntermediate currNode;
private final LinkedList<Throwable> errors = new LinkedList<>();
private boolean parsingOpenGoal = false;

/**
* @param proof Proof object for storing meta information about the parsed proof.
Expand Down Expand Up @@ -220,6 +221,9 @@ public void beginExpr(ProofElementID eid, String str) {
}
}
case SOLVERTYPE -> ((BuiltinRuleInformation) ruleInfo).solver = str;
case OPEN_GOAL -> {
parsingOpenGoal = true;
}
default -> {
}
}
Expand All @@ -235,6 +239,24 @@ public void endExpr(ProofElementID eid, int lineNr) {
((AppNodeIntermediate) currNode).setInteractiveRuleApplication(true);
}
}
case SELECTED_NODE -> {
int openGoalChildSelected = -1;
if (parsingOpenGoal) {
if (currNode instanceof BranchNodeIntermediate branchNode) {
if (!stack.isEmpty()) {
if (stack.peek() instanceof AppNodeIntermediate parentAppNode) {
openGoalChildSelected = parentAppNode.getChildren().size() - 1;
parentAppNode.setSelectedNode(true, openGoalChildSelected);
}
}
} else {
openGoalChildSelected = 0;
}
}
if (currNode != null && currNode instanceof AppNodeIntermediate appNode) {
appNode.setSelectedNode(true, openGoalChildSelected);
}
}
case PROOF_SCRIPT -> {
if (currNode != null) {
((AppNodeIntermediate) currNode).setScriptRuleApplication(true);
Expand All @@ -254,6 +276,9 @@ public void endExpr(ProofElementID eid, int lineNr) {
builtinInfo.builtinIfInsts.append(new Pair<>(
builtinInfo.currIfInstFormula, builtinInfo.currIfInstPosInTerm));
}
case OPEN_GOAL -> {
parsingOpenGoal = false;
}
default -> {
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,12 +116,15 @@ public class IntermediateProofReplayer {
/** The current open goal */
private Goal currGoal = null;

/** the node selected at time of saving, or null if information is not available */
private Node savedSelectedNode = null;

/**
* Constructs a new {@link IntermediateProofReplayer}.
*
* @param loader The problem loader, for reporting errors.
* @param proof The proof object into which to load the replayed proof.
* @param parserResult the result of the proof file parser to be replayed
* @param loader the problem loader, for reporting errors.
* @param proof the proof object into which to load the replayed proof.
* @param parserResult the result of the intermediate parser to be replayed
*/
public IntermediateProofReplayer(AbstractProblemLoader loader, Proof proof,
IntermediatePresentationProofFileParser.Result parserResult) {
Expand Down Expand Up @@ -223,6 +226,7 @@ public Result replay(ProblemInitializer.ProblemInitializerListener listener,
proof.getServices().getNameRecorder()
.setProposals(currInterm.getIntermediateRuleApp().getNewNames());


if (currInterm.getIntermediateRuleApp() instanceof TacletAppIntermediate) {
TacletAppIntermediate appInterm =
(TacletAppIntermediate) currInterm.getIntermediateRuleApp();
Expand Down Expand Up @@ -358,6 +362,21 @@ public Result replay(ProblemInitializer.ProblemInitializerListener listener,
}
}
}
// set information if this node was the last selected node at the time of saving
// the
// proof
if (currInterm.getSelectedNode()) {
// check whether the node itself was selected or a child that is an open
// goal
int openChildSelected = currInterm.getOpenChildSelected();
if (openChildSelected < 0
|| openChildSelected >= currNode.childrenCount()) {
savedSelectedNode = currNode;
} else {
savedSelectedNode = currNode.child(openChildSelected);
}
}

}
} catch (Throwable throwable) {
// Default exception catcher -- proof should not stop loading
Expand All @@ -374,7 +393,7 @@ public Result replay(ProblemInitializer.ProblemInitializerListener listener,
progressMonitor.setProgress(max);
}
LOGGER.debug("Proof replay took " + PerfScope.formatTime(System.nanoTime() - time));
return new Result(status, errors, currGoal);
return new Result(status, errors, currGoal, savedSelectedNode);
}

/**
Expand Down Expand Up @@ -1057,27 +1076,6 @@ static class SkipSMTRuleException extends Exception {
*
* @author Dominic Scheurer
*/
public static class Result {
private final String status;
private final List<Throwable> errors;
private Goal lastSelectedGoal = null;

public Result(String status, List<Throwable> errors, Goal lastSelectedGoal) {
this.status = status;
this.errors = errors;
this.lastSelectedGoal = lastSelectedGoal;
}

public String getStatus() {
return status;
}

public List<Throwable> getErrors() {
return errors;
}

public Goal getLastSelectedGoal() {
return lastSelectedGoal;
}
}
public record Result(String status, List<Throwable> errors, Goal lastSelectedGoal,
Node savedSelectedNode) {}
}
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ public class OutputStreamProofSaver {
*/
protected final boolean saveProofSteps;

/**
* The currently selected node if available, otherwise null
*/
protected final Node selectedNode;

/**
* Extracts java source directory from {@link Proof#header()}, if it exists.
Expand All @@ -95,27 +99,28 @@ public static File getJavaSourceLocation(Proof proof) {
return null;
}

public OutputStreamProofSaver(Proof proof) {
this(proof, KeYConstants.INTERNAL_VERSION);
public OutputStreamProofSaver(Proof proof, Node selectedNode) {
this(proof, selectedNode, KeYConstants.INTERNAL_VERSION);
}

public OutputStreamProofSaver(Proof proof, String internalVersion) {
this.proof = proof;
this.internalVersion = internalVersion;
this.saveProofSteps = true;
public OutputStreamProofSaver(Proof proof, Node selectedNode, String internalVersion) {
this(proof, selectedNode, internalVersion, true);
}

/**
* Create a new OutputStreamProofSaver.
*
* @param proof the proof to save
* @param selectedNode the Node selected at time of saving or null if no information available
* @param internalVersion currently running KeY version
* @param saveProofSteps whether to save the performed proof steps
*/
public OutputStreamProofSaver(Proof proof, String internalVersion, boolean saveProofSteps) {
public OutputStreamProofSaver(Proof proof, Node selectedNode, String internalVersion,
boolean saveProofSteps) {
this.proof = proof;
this.internalVersion = internalVersion;
this.saveProofSteps = saveProofSteps;
this.selectedNode = selectedNode;
}

/**
Expand Down Expand Up @@ -589,7 +594,11 @@ private void printSingleNode(Node node, String prefix, Appendable output) throws

printer.printSequent(node.sequent());
output.append(escapeCharacters(printer.result().replace('\n', ' ')));
output.append("\")\n");
output.append("\"");
if (node == selectedNode) {
output.append(" (" + ProofElementID.SELECTED_NODE.getRawName() + ")");

Check warning on line 599 in key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java

View workflow job for this annotation

GitHub Actions / qodana

String concatenation as argument to 'StringBuilder.append()' call

String concatenation as argument to `Appendable.append()` call
}
output.append(")\n");
return;
}

Expand Down Expand Up @@ -653,10 +662,13 @@ private void collectProof(Node node, String prefix, Appendable output) throws IO
*/
private void userInteraction2Proof(Node node, Appendable output) throws IOException {
if (node.getNodeInfo().getInteractiveRuleApplication()) {
output.append(" (userinteraction)");
output.append(" (" + ProofElementID.USER_INTERACTION.getRawName() + ")");

Check warning on line 665 in key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java

View workflow job for this annotation

GitHub Actions / qodana

String concatenation as argument to 'StringBuilder.append()' call

String concatenation as argument to `Appendable.append()` call
}
if (node == selectedNode) {
output.append(" (" + ProofElementID.SELECTED_NODE.getRawName() + ")");

Check warning on line 668 in key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java

View workflow job for this annotation

GitHub Actions / qodana

String concatenation as argument to 'StringBuilder.append()' call

String concatenation as argument to `Appendable.append()` call
}
if (node.getNodeInfo().getScriptRuleApplication()) {
output.append(" (proofscript)");
output.append(" (" + ProofElementID.PROOF_SCRIPT.getRawName() + ")");

Check warning on line 671 in key.core/src/main/java/de/uka/ilkd/key/proof/io/OutputStreamProofSaver.java

View workflow job for this annotation

GitHub Actions / qodana

String concatenation as argument to 'StringBuilder.append()' call

String concatenation as argument to `Appendable.append()` call
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import java.io.IOException;
import java.nio.file.Paths;

import de.uka.ilkd.key.proof.Node;
import de.uka.ilkd.key.proof.Proof;
import de.uka.ilkd.key.proof.io.consistency.AbstractFileRepo;
import de.uka.ilkd.key.proof.io.consistency.FileRepo;
Expand All @@ -30,10 +31,11 @@ public class ProofBundleSaver extends ProofSaver {
* Creates a new ProofBundleSaver.
*
* @param proof the proof to save
* @param selectedNode the Node selected at time of saving or null if no information available
* @param saveFile the target filename
*/
public ProofBundleSaver(Proof proof, File saveFile) {
super(proof, saveFile);
public ProofBundleSaver(Proof proof, Node selectedNode, File saveFile) {
super(proof, selectedNode, saveFile);
}

@Override
Expand Down
22 changes: 12 additions & 10 deletions key.core/src/main/java/de/uka/ilkd/key/proof/io/ProofSaver.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
import java.util.LinkedList;
import java.util.List;

import de.uka.ilkd.key.proof.Node;
import de.uka.ilkd.key.proof.Proof;
import de.uka.ilkd.key.proof.io.event.ProofSaverEvent;
import de.uka.ilkd.key.proof.io.event.ProofSaverListener;
Expand Down Expand Up @@ -37,16 +38,16 @@ public class ProofSaver extends OutputStreamProofSaver {
*/
private static final List<ProofSaverListener> listeners = new LinkedList<>();

public ProofSaver(Proof proof, String fileName, String internalVersion) {
this(proof, new File(fileName), internalVersion);
public ProofSaver(Proof proof, Node selectedNdode, String fileName, String internalVersion) {
this(proof, selectedNdode, new File(fileName), internalVersion);
}

public ProofSaver(Proof proof, File file) {
this(proof, file, KeYConstants.INTERNAL_VERSION);
public ProofSaver(Proof proof, Node selectedNode, File file) {
this(proof, selectedNode, file, KeYConstants.INTERNAL_VERSION);
}

public ProofSaver(Proof proof, File file, String internalVersion) {
super(proof, internalVersion);
public ProofSaver(Proof proof, Node selectedNode, File file, String internalVersion) {
super(proof, selectedNode, internalVersion);
this.file = file;
}

Expand All @@ -57,8 +58,8 @@ public ProofSaver(Proof proof, File file, String internalVersion) {
* @param file file to save proof into
* @param saveProofSteps whether to save proof steps (false -> only proof obligation)
*/
public ProofSaver(Proof proof, File file, boolean saveProofSteps) {
this(proof, file, KeYConstants.INTERNAL_VERSION, saveProofSteps);
public ProofSaver(Proof proof, Node selectedNode, File file, boolean saveProofSteps) {
this(proof, selectedNode, file, KeYConstants.INTERNAL_VERSION, saveProofSteps);
}

/**
Expand All @@ -69,8 +70,9 @@ public ProofSaver(Proof proof, File file, boolean saveProofSteps) {
* @param internalVersion version of KeY to add to the proof log
* @param saveProofSteps whether to save proof steps (false -> only proof obligation)
*/
public ProofSaver(Proof proof, File file, String internalVersion, boolean saveProofSteps) {
super(proof, internalVersion, saveProofSteps);
public ProofSaver(Proof proof, Node selectedNode, File file, String internalVersion,
boolean saveProofSteps) {
super(proof, selectedNode, internalVersion, saveProofSteps);
this.file = file;
}

Expand Down
Loading

0 comments on commit 638e108

Please sign in to comment.