From 07714bb8fda877ab830a0605a592a00765b5fd09 Mon Sep 17 00:00:00 2001
From: dgelessus <dgelessus@users.noreply.github.com>
Date: Thu, 19 Jul 2018 17:38:24 +0200
Subject: [PATCH] Support inspection of identifiers in B expressions

---
 .../prob2/jupyter/commands/CommandUtils.java  | 82 ++++++++++++++++---
 .../prob2/jupyter/commands/EvalCommand.java   |  6 ++
 2 files changed, 76 insertions(+), 12 deletions(-)

diff --git a/src/main/java/de/prob2/jupyter/commands/CommandUtils.java b/src/main/java/de/prob2/jupyter/commands/CommandUtils.java
index aca5a81..0dbdbf1 100644
--- a/src/main/java/de/prob2/jupyter/commands/CommandUtils.java
+++ b/src/main/java/de/prob2/jupyter/commands/CommandUtils.java
@@ -20,7 +20,10 @@ import de.prob.animator.domainobjects.ComputationNotCompletedResult;
 import de.prob.animator.domainobjects.EnumerationWarning;
 import de.prob.animator.domainobjects.EvalResult;
 import de.prob.animator.domainobjects.EvaluationErrorResult;
+import de.prob.animator.domainobjects.FormulaExpand;
+import de.prob.animator.domainobjects.IEvalElement;
 import de.prob.animator.domainobjects.IdentifierNotInitialised;
+import de.prob.animator.domainobjects.TypeCheckResult;
 import de.prob.animator.domainobjects.WDError;
 import de.prob.exception.ProBError;
 import de.prob.statespace.Trace;
@@ -241,29 +244,84 @@ public final class CommandUtils {
 		return replacements == null ? null : offsetReplacementOptions(replacements, argStart);
 	}
 	
-	public static @NotNull ReplacementOptions completeInBExpression(final @NotNull Trace trace, final @NotNull String code, final int at) {
+	public static @Nullable Matcher matchBIdentifierAt(final @NotNull String code, final int at) {
 		final Matcher identifierMatcher = B_IDENTIFIER_PATTERN.matcher(code);
-		String identifier = "";
-		int start = at;
-		int end = at;
-		// Try to find the identifier that the cursor is in.
-		// If the cursor is not on an identifier, default to empty string, i. e. show all possible completions.
 		while (identifierMatcher.find() && identifierMatcher.start() < at) {
 			if (identifierMatcher.end() >= at) {
-				identifier = code.substring(identifierMatcher.start(), at);
-				start = identifierMatcher.start();
-				end = identifierMatcher.end();
-				break;
+				return identifierMatcher;
 			}
 		}
+		return null;
+	}
+	
+	public static @Nullable DisplayData inspectInBExpression(final @NotNull Trace trace, final @NotNull String code, final int at) {
+		final Matcher identifierMatcher = CommandUtils.matchBIdentifierAt(code, at);
+		if (identifierMatcher == null) {
+			return null;
+		}
+		final String identifier = identifierMatcher.group();
+		final IEvalElement formula = trace.getModel().parseFormula(identifier, FormulaExpand.TRUNCATE);
+		
+		final StringBuilder sbPlain = new StringBuilder();
+		final StringBuilder sbMarkdown = new StringBuilder();
+		sbPlain.append(UnicodeTranslator.toUnicode(identifier));
+		sbPlain.append('\n');
+		sbMarkdown.append('$');
+		sbMarkdown.append(UnicodeTranslator.toLatex(identifier));
+		sbMarkdown.append("$  \n");
+		
+		final TypeCheckResult type = trace.getStateSpace().typeCheck(formula);
+		sbPlain.append("Type: ");
+		sbMarkdown.append("**Type:** ");
+		if (type.isOk()) {
+			sbPlain.append(type.getType());
+			sbMarkdown.append('`');
+			sbMarkdown.append(type.getType());
+			sbMarkdown.append('`');
+		} else {
+			sbPlain.append("Type error: ");
+			sbPlain.append(type.getErrors());
+			sbMarkdown.append("*Type error:* ");
+			sbMarkdown.append(type.getErrors());
+		}
+		sbPlain.append('\n');
+		sbMarkdown.append("  \n");
+		
+		final AbstractEvalResult aer = trace.evalCurrent(formula);
+		sbPlain.append("Current value: ");
+		sbPlain.append(CommandUtils.inlinePlainTextForEvalResult(aer));
+		sbMarkdown.append("**Current value:** ");
+		sbMarkdown.append(inlineMarkdownForEvalResult(aer));
+		
+		final DisplayData result = new DisplayData(sbPlain.toString());
+		result.putMarkdown(sbMarkdown.toString());
+		return result;
+	}
+	
+	public static @NotNull ReplacementOptions completeInBExpression(final @NotNull Trace trace, final @NotNull String code, final int at) {
+		final Matcher identifierMatcher = matchBIdentifierAt(code, at);
+		// Try to find the identifier that the cursor is in.
+		// If the cursor is not on an identifier, default to empty string, i. e. show all possible completions.
+		String prefix;
+		int start;
+		int end;
+		if (identifierMatcher == null) {
+			prefix = "";
+			start = at;
+			end = at;
+		} else {
+			prefix = code.substring(identifierMatcher.start(), at);
+			start = identifierMatcher.start();
+			end = identifierMatcher.end();
+		}
 		
-		final CompleteIdentifierCommand cmdExact = new CompleteIdentifierCommand(identifier);
+		final CompleteIdentifierCommand cmdExact = new CompleteIdentifierCommand(prefix);
 		cmdExact.setIncludeKeywords(true);
 		trace.getStateSpace().execute(cmdExact);
 		// Use LinkedHashSet to remove duplicates while maintaining order.
 		final Set<String> completions = new LinkedHashSet<>(cmdExact.getCompletions());
 		
-		final CompleteIdentifierCommand cmdIgnoreCase = new CompleteIdentifierCommand(identifier);
+		final CompleteIdentifierCommand cmdIgnoreCase = new CompleteIdentifierCommand(prefix);
 		cmdIgnoreCase.setIgnoreCase(true);
 		cmdIgnoreCase.setIncludeKeywords(true);
 		trace.getStateSpace().execute(cmdIgnoreCase);
diff --git a/src/main/java/de/prob2/jupyter/commands/EvalCommand.java b/src/main/java/de/prob2/jupyter/commands/EvalCommand.java
index 8d2e75c..f842104 100644
--- a/src/main/java/de/prob2/jupyter/commands/EvalCommand.java
+++ b/src/main/java/de/prob2/jupyter/commands/EvalCommand.java
@@ -9,6 +9,7 @@ import io.github.spencerpark.jupyter.kernel.ReplacementOptions;
 import io.github.spencerpark.jupyter.kernel.display.DisplayData;
 
 import org.jetbrains.annotations.NotNull;
+import org.jetbrains.annotations.Nullable;
 
 public final class EvalCommand implements Command {
 	private final @NotNull AnimationSelector animationSelector;
@@ -41,6 +42,11 @@ public final class EvalCommand implements Command {
 		return CommandUtils.displayDataForEvalResult(CommandUtils.withSourceCode(argString, () -> this.animationSelector.getCurrentTrace().evalCurrent(argString, FormulaExpand.EXPAND)));
 	}
 	
+	@Override
+	public @Nullable DisplayData inspect(final @NotNull String argString, final int at) {
+		return CommandUtils.inspectInBExpression(this.animationSelector.getCurrentTrace(), argString, at);
+	}
+	
 	@Override
 	public @NotNull ReplacementOptions complete(final @NotNull String argString, final int at) {
 		return CommandUtils.completeInBExpression(this.animationSelector.getCurrentTrace(), argString, at);
-- 
GitLab