diff --git a/notebooks/tests/render.ipynb b/notebooks/tests/render.ipynb index 283f29a3532d72c08be07a84010ff58d90ba8176..308e8d3aef019469c9164264289a3216951c56da 100644 --- a/notebooks/tests/render.ipynb +++ b/notebooks/tests/render.ipynb @@ -203,10 +203,10 @@ "outputs": [ { "ename": "CommandExecutionException", - "evalue": "::render: Missing content (the content cannot be placed on the same line as the command)", + "evalue": "::render: Missing required body content", "output_type": "error", "traceback": [ - "\u001b[1m\u001b[31m::render: Missing content (the content cannot be placed on the same line as the command)\u001b[0m" + "\u001b[1m\u001b[31m::render: Missing required body content\u001b[0m" ] } ], @@ -228,10 +228,10 @@ "outputs": [ { "ename": "CommandExecutionException", - "evalue": "::render: Missing MIME type", + "evalue": "::render: Missing required parameter mimeType", "output_type": "error", "traceback": [ - "\u001b[1m\u001b[31m::render: Missing MIME type\u001b[0m" + "\u001b[1m\u001b[31m::render: Missing required parameter mimeType\u001b[0m" ] } ], diff --git a/src/main/java/de/prob2/jupyter/CommandUtils.java b/src/main/java/de/prob2/jupyter/CommandUtils.java index 3c83d96eb6a868411d65c2ff03b0d2141f36c78c..f5694b7dd4f97f771e6fe199e6079e280de5c05e 100644 --- a/src/main/java/de/prob2/jupyter/CommandUtils.java +++ b/src/main/java/de/prob2/jupyter/CommandUtils.java @@ -120,7 +120,19 @@ public final class CommandUtils { public static @NotNull ParsedArguments parseArgs(final @NotNull Parameters parameters, final @NotNull String argString) { final ParsedArguments parsed = new ParsedArguments(Collections.emptyMap()); - String remainingArgs = argString; + String remainingArgs; + if (parameters.getBodyParam().isPresent()) { + final String[] split = argString.split("\n", 2); + final PositionalParameter.RequiredRemainder bodyParam = parameters.getBodyParam().get(); + if (split.length < 2) { + throw new UserErrorException("Missing required body " + bodyParam.getIdentifier()); + } + remainingArgs = split[0]; + parsed.put(bodyParam, split[1]); + } else { + remainingArgs = argString; + } + for (final PositionalParameter<?> param : parameters.getPositionalParameters()) { if (remainingArgs.isEmpty()) { break; diff --git a/src/main/java/de/prob2/jupyter/Parameters.java b/src/main/java/de/prob2/jupyter/Parameters.java index 0ef54274217db75f79a4a4970ddf06e02b439223..d004b3583388e36e27357505724bdb7c7455fa26 100644 --- a/src/main/java/de/prob2/jupyter/Parameters.java +++ b/src/main/java/de/prob2/jupyter/Parameters.java @@ -2,15 +2,18 @@ package de.prob2.jupyter; import java.util.Collections; import java.util.List; +import java.util.Optional; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; public final class Parameters { public static final @NotNull Parameters NONE = new Parameters(Collections.emptyList()); private final @NotNull List<PositionalParameter<?>> positionalParameters; + private final @Nullable PositionalParameter.RequiredRemainder bodyParam; - public Parameters(final @NotNull List<PositionalParameter<?>> positionalParameters) { + public Parameters(final @NotNull List<PositionalParameter<?>> positionalParameters, final @Nullable PositionalParameter.RequiredRemainder bodyParam) { super(); this.positionalParameters = positionalParameters; @@ -29,9 +32,19 @@ public final class Parameters { seenOptional |= isOptional; seenOnlyLast |= isOnlyLast; } + + this.bodyParam = bodyParam; + } + + public Parameters(final @NotNull List<PositionalParameter<?>> positionalParameters) { + this(positionalParameters, null); } public @NotNull List<PositionalParameter<?>> getPositionalParameters() { return this.positionalParameters; } + + public @NotNull Optional<PositionalParameter.RequiredRemainder> getBodyParam() { + return Optional.ofNullable(this.bodyParam); + } } diff --git a/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java b/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java index 3f115cb5de179fae11f89ac8c5b2384a9a71a8fd..ac346823e1caa7dede37158de6ad746a0fcb878e 100644 --- a/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java +++ b/src/main/java/de/prob2/jupyter/commands/LoadCellCommand.java @@ -17,7 +17,6 @@ import de.prob2.jupyter.Parameters; import de.prob2.jupyter.ParsedArguments; import de.prob2.jupyter.PositionalParameter; 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; @@ -26,7 +25,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public final class LoadCellCommand implements Command { - private static final @NotNull PositionalParameter.RequiredRemainder PREFS_AND_CODE_PARAM = new PositionalParameter.RequiredRemainder("prefsAndCode"); + private static final @NotNull PositionalParameter.OptionalRemainder PREFS_PARAM = new PositionalParameter.OptionalRemainder("prefs"); + private static final @NotNull PositionalParameter.RequiredRemainder CODE_PARAM = new PositionalParameter.RequiredRemainder("code"); private final @NotNull ClassicalBFactory classicalBFactory; private final @NotNull AnimationSelector animationSelector; @@ -52,7 +52,7 @@ public final class LoadCellCommand implements Command { @Override public @NotNull Parameters getParameters() { - return new Parameters(Collections.singletonList(PREFS_AND_CODE_PARAM)); + return new Parameters(Collections.singletonList(PREFS_PARAM), CODE_PARAM); } @Override @@ -74,13 +74,8 @@ public final class LoadCellCommand implements Command { @Override public @NotNull DisplayData run(final @NotNull ParsedArguments args) { - final String[] split = args.get(PREFS_AND_CODE_PARAM).split("\n", 2); - if (split.length != 2) { - throw new UserErrorException("Missing command body"); - } - final String prefsString = split[0]; - final String body = split[1]; - final List<String> prefsSplit = CommandUtils.splitArgs(prefsString); + final String body = args.get(CODE_PARAM); + final List<String> prefsSplit = args.get(PREFS_PARAM).map(CommandUtils::splitArgs).orElse(Collections.emptyList()); final Map<String, String> preferences = CommandUtils.parsePreferences(prefsSplit); this.animationSelector.changeCurrentAnimation(new Trace(CommandUtils.withSourceCode(body, () -> diff --git a/src/main/java/de/prob2/jupyter/commands/RenderCommand.java b/src/main/java/de/prob2/jupyter/commands/RenderCommand.java index 4e3785592479efbe57ba51d6fcc805452ce26887..22e4d2bae5e29ca65e3e985bb4e8afad387d19c0 100644 --- a/src/main/java/de/prob2/jupyter/commands/RenderCommand.java +++ b/src/main/java/de/prob2/jupyter/commands/RenderCommand.java @@ -8,7 +8,6 @@ import de.prob2.jupyter.Command; import de.prob2.jupyter.Parameters; import de.prob2.jupyter.ParsedArguments; import de.prob2.jupyter.PositionalParameter; -import de.prob2.jupyter.UserErrorException; import io.github.spencerpark.jupyter.kernel.ReplacementOptions; import io.github.spencerpark.jupyter.kernel.display.DisplayData; @@ -17,7 +16,8 @@ import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; public final class RenderCommand implements Command { - private static final @NotNull PositionalParameter.RequiredRemainder MIME_TYPE_AND_CONTENT_PARAM = new PositionalParameter.RequiredRemainder("mimeTypeAndContent"); + private static final @NotNull PositionalParameter.RequiredSingle MIME_TYPE_PARAM = new PositionalParameter.RequiredSingle("mimeType"); + private static final @NotNull PositionalParameter.RequiredRemainder CONTENT_PARAM = new PositionalParameter.RequiredRemainder("content"); @Inject private RenderCommand() { @@ -31,7 +31,7 @@ public final class RenderCommand implements Command { @Override public @NotNull Parameters getParameters() { - return new Parameters(Collections.singletonList(MIME_TYPE_AND_CONTENT_PARAM)); + return new Parameters(Collections.singletonList(MIME_TYPE_PARAM), CONTENT_PARAM); } @Override @@ -52,15 +52,8 @@ public final class RenderCommand implements Command { @Override public @NotNull DisplayData run(final @NotNull ParsedArguments args) { - final String[] split = args.get(MIME_TYPE_AND_CONTENT_PARAM).split("\n", 2); - if (split.length != 2) { - throw new UserErrorException("Missing content (the content cannot be placed on the same line as the command)"); - } - final String mimeType = split[0]; - final String code = split[1]; - if (mimeType.isEmpty()) { - throw new UserErrorException("Missing MIME type"); - } + final String mimeType = args.get(MIME_TYPE_PARAM); + final String code = args.get(CONTENT_PARAM); final DisplayData data = new DisplayData(mimeType + ":\n" + code); data.putData(mimeType, code); return data;