From 2fe2472667811dfbdbae73311f63e272e20f6fa5 Mon Sep 17 00:00:00 2001 From: dgelessus <dgelessus@users.noreply.github.com> Date: Thu, 21 Jun 2018 15:47:34 +0200 Subject: [PATCH] Add code completion support to ::load, :load, and :pref --- .../prob2/jupyter/commands/CommandUtils.java | 22 +++++++++ .../jupyter/commands/LoadCellCommand.java | 14 ++++++ .../jupyter/commands/LoadFileCommand.java | 45 +++++++++++++++++++ .../prob2/jupyter/commands/PrefCommand.java | 7 +++ 4 files changed, 88 insertions(+) diff --git a/src/main/java/de/prob2/jupyter/commands/CommandUtils.java b/src/main/java/de/prob2/jupyter/commands/CommandUtils.java index 4aa1ffc..28cce18 100644 --- a/src/main/java/de/prob2/jupyter/commands/CommandUtils.java +++ b/src/main/java/de/prob2/jupyter/commands/CommandUtils.java @@ -10,8 +10,10 @@ import java.util.Map; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; +import java.util.stream.Collectors; import de.prob.animator.command.CompleteIdentifierCommand; +import de.prob.animator.command.GetCurrentPreferencesCommand; import de.prob.animator.domainobjects.AbstractEvalResult; import de.prob.animator.domainobjects.ComputationNotCompletedResult; import de.prob.animator.domainobjects.EnumerationWarning; @@ -26,6 +28,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; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -192,4 +195,23 @@ public final class CommandUtils { return new ReplacementOptions(new ArrayList<>(completions), start, end); } + + public static @Nullable ReplacementOptions completeInPreferences(final @NotNull Trace trace, final @NotNull String code, final int at) { + final Matcher argSplitMatcher = ARG_SPLIT_PATTERN.matcher(code); + int prefNameStart = 0; + while (argSplitMatcher.find() && argSplitMatcher.end() <= at) { + prefNameStart = argSplitMatcher.end(); + } + final Matcher prefNameMatcher = B_IDENTIFIER_PATTERN.matcher(code); + prefNameMatcher.region(prefNameStart, code.length()); + if (prefNameMatcher.lookingAt() && at <= prefNameMatcher.end()) { + final String prefix = code.substring(prefNameMatcher.start(), at); + final GetCurrentPreferencesCommand cmd = new GetCurrentPreferencesCommand(); + trace.getStateSpace().execute(cmd); + final List<String> prefs = cmd.getPreferences().keySet().stream().filter(s -> s.startsWith(prefix)).sorted().collect(Collectors.toList()); + return new ReplacementOptions(prefs, prefNameMatcher.start(), prefNameMatcher.end()); + } else { + return null; + } + } } diff --git a/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java b/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java index ff2cae4..19b5c52 100644 --- a/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java +++ b/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java @@ -12,9 +12,11 @@ import de.prob.statespace.Trace; import de.prob2.jupyter.ProBKernel; import de.prob2.jupyter.UserErrorException; +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 LoadCellCommand implements Command { private final @NotNull ClassicalBFactory classicalBFactory; @@ -52,4 +54,16 @@ public final class LoadCellCommand implements Command { this.animationSelector.changeCurrentAnimation(new Trace(this.classicalBFactory.create(body).load(preferences))); return new DisplayData("Loaded machine: " + this.animationSelector.getCurrentTrace().getModel()); } + + @Override + public @Nullable ReplacementOptions complete(final @NotNull ProBKernel kernel, final @NotNull String argString, final int at) { + final int newlinePos = argString.indexOf('\n'); + if (newlinePos == -1 || at < newlinePos) { + // Cursor is on the first line, provide preference name completions. + return CommandUtils.completeInPreferences(this.animationSelector.getCurrentTrace(), argString, at); + } else { + // Cursor is in the body, provide B completions. + return CommandUtils.completeInBExpression(this.animationSelector.getCurrentTrace(), argString, at); + } + } } diff --git a/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java b/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java index 2a4d7c6..e1c93ab 100644 --- a/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java +++ b/src/main/java/de/prob2/jupyter/commands/LoadFileCommand.java @@ -1,8 +1,14 @@ package de.prob2.jupyter.commands; import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; import java.util.List; import java.util.Map; +import java.util.regex.Matcher; +import java.util.stream.Collectors; +import java.util.stream.Stream; import com.google.inject.Inject; @@ -14,11 +20,18 @@ import de.prob.statespace.Trace; import de.prob2.jupyter.ProBKernel; import de.prob2.jupyter.UserErrorException; +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; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; public final class LoadFileCommand implements Command { + private static final @NotNull Logger LOGGER = LoggerFactory.getLogger(LoadFileCommand.class); + private final @NotNull ClassicalBFactory classicalBFactory; private final @NotNull AnimationSelector animationSelector; @@ -57,4 +70,36 @@ public final class LoadFileCommand implements Command { } return new DisplayData("Loaded machine: " + this.animationSelector.getCurrentTrace().getModel()); } + + @Override + public @Nullable ReplacementOptions complete(final @NotNull ProBKernel kernel, final @NotNull String argString, final int at) { + final int fileNameEnd; + final Matcher argSplitMatcher = CommandUtils.ARG_SPLIT_PATTERN.matcher(argString); + if (argSplitMatcher.find()) { + fileNameEnd = argSplitMatcher.start(); + } else { + fileNameEnd = argString.length(); + } + + if (fileNameEnd < at) { + // Cursor is in the preferences, provide preference name completions. + final ReplacementOptions replacements = CommandUtils.completeInPreferences(this.animationSelector.getCurrentTrace(), argString.substring(fileNameEnd), at - fileNameEnd); + return replacements == null ? null : CommandUtils.offsetReplacementOptions(replacements, fileNameEnd); + } else { + // Cursor is in the file name, provide machine files from the current directory as completions. + final String prefix = argString.substring(0, at); + final List<String> fileNames; + try (final Stream<Path> list = Files.list(Paths.get(""))) { + fileNames = list + .map(Path::getFileName) + .map(Object::toString) + .filter(s -> s.startsWith(prefix) && (s.endsWith(".mch") || s.endsWith(".ref") || s.endsWith(".imp"))) + .collect(Collectors.toList()); + } catch (final IOException e) { + LOGGER.warn("Could not list contents of the current directory, cannot provide file name completions for :load", e); + return null; + } + return new ReplacementOptions(fileNames, 0, fileNameEnd); + } + } } diff --git a/src/main/java/de/prob2/jupyter/commands/PrefCommand.java b/src/main/java/de/prob2/jupyter/commands/PrefCommand.java index f8885d1..43d9bcb 100644 --- a/src/main/java/de/prob2/jupyter/commands/PrefCommand.java +++ b/src/main/java/de/prob2/jupyter/commands/PrefCommand.java @@ -15,9 +15,11 @@ import de.prob.statespace.AnimationSelector; import de.prob2.jupyter.ProBKernel; import de.prob2.jupyter.UserErrorException; +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 PrefCommand implements Command { private final @NotNull AnimationSelector animationSelector; @@ -82,4 +84,9 @@ public final class PrefCommand implements Command { } return new DisplayData(sb.toString()); } + + @Override + public @Nullable ReplacementOptions complete(final @NotNull ProBKernel kernel, final @NotNull String argString, final int at) { + return CommandUtils.completeInPreferences(this.animationSelector.getCurrentTrace(), argString, at); + } } -- GitLab