Skip to content
Snippets Groups Projects
Commit fb9f43e6 authored by dgelessus's avatar dgelessus
Browse files

Unify handling of command-less input in ProBKernel

There is now a single preprocessInput method that is responsible for
adding a command prefix to any input that doesn't already have one.
This simplifies the implementations of (eval|inspect|complete)Internal
somewhat, because they can now assume that the input (after
preprocessing) always starts with a command name.
parent d083be85
Branches
Tags
No related merge requests found
...@@ -322,27 +322,47 @@ public final class ProBKernel extends BaseKernel { ...@@ -322,27 +322,47 @@ public final class ProBKernel extends BaseKernel {
return MACHINE_CODE_PATTERN.matcher(code).matches(); return MACHINE_CODE_PATTERN.matcher(code).matches();
} }
private @Nullable DisplayData evalInternal(final @NotNull PositionedString code) { /**
* Preprocess the given input by ensuring that it starts with a command name.
* If a command name is already present,
* the input is returned unchanged.
* Otherwise,
* an appropriate command is added based on the type of input.
*
* @param code the input code to preprocess
* @return the input with a command name prefixed if necessary
*/
private static @NotNull PositionedString preprocessInput(final @NotNull PositionedString code) {
final Matcher commandMatcher = COMMAND_PATTERN.matcher(code.getValue()); final Matcher commandMatcher = COMMAND_PATTERN.matcher(code.getValue());
final PositionedString name; final String prefix;
final PositionedString argString;
if (commandMatcher.matches()) { if (commandMatcher.matches()) {
// The input is a command, execute it directly. // The input already includes a command, so no prefix needs to be added.
name = code.substring(commandMatcher.start(1), commandMatcher.end(1)); prefix = "";
if (commandMatcher.group(2) == null) { } else if (isMachineCode(code.getValue())) {
argString = code.substring(code.getValue().length()); // The input appears to be a machine, add a command to load it.
prefix = "::load\n";
} else { } else {
argString = code.substring(commandMatcher.start(2), commandMatcher.end(2)); // By default, assume that the input is an expression that should be evaluated.
prefix = ":eval ";
} }
} else if (isMachineCode(code.getValue())) { // Add the prefix and adjust the start position.
// The input appears to be a machine, load it. // This means that the characters from the prefix (if any) will have negative positions,
// The leading newline here is important. ::load expects the first input line to contain preference assignments; the actual machine code has to start on the second line. // but the characters from the real source code will have the same positions as before.
name = new PositionedString("::load", code.getStartPosition() - 7); return new PositionedString(prefix + code.getValue(), code.getStartPosition() - prefix.length());
argString = new PositionedString("\n" + code.getValue(), code.getStartPosition() - 1); }
private @Nullable DisplayData evalInternal(final @NotNull PositionedString code) {
final PositionedString preprocessedCode = preprocessInput(code);
final Matcher commandMatcher = COMMAND_PATTERN.matcher(preprocessedCode.getValue());
if (!commandMatcher.matches()) {
throw new AssertionError("Preprocessed input does not include a command - this should not happen");
}
final PositionedString name = preprocessedCode.substring(commandMatcher.start(1), commandMatcher.end(1));
final PositionedString argString;
if (commandMatcher.group(2) == null) {
argString = preprocessedCode.substring(preprocessedCode.getValue().length());
} else { } else {
// By default, assume that the input is an expression and evaluate it. argString = preprocessedCode.substring(commandMatcher.start(2), commandMatcher.end(2));
name = new PositionedString(":eval", code.getStartPosition() - 6);
argString = code;
} }
return this.executeCommand(name, argString); return this.executeCommand(name, argString);
} }
...@@ -378,16 +398,18 @@ public final class ProBKernel extends BaseKernel { ...@@ -378,16 +398,18 @@ public final class ProBKernel extends BaseKernel {
} }
private @Nullable DisplayData inspectInternal(final @NotNull PositionedString code, final int at) { private @Nullable DisplayData inspectInternal(final @NotNull PositionedString code, final int at) {
final Matcher commandMatcher = COMMAND_PATTERN.matcher(code.getValue()); final PositionedString preprocessedCode = preprocessInput(code);
if (commandMatcher.matches()) { final Matcher commandMatcher = COMMAND_PATTERN.matcher(preprocessedCode.getValue());
// The code is a valid command. if (!commandMatcher.matches()) {
throw new AssertionError("Preprocessed input does not include a command - this should not happen");
}
final String name = commandMatcher.group(1); final String name = commandMatcher.group(1);
if (this.getCommands().containsKey(name)) { if (this.getCommands().containsKey(name)) {
final Command command = this.getCommands().get(name); final Command command = this.getCommands().get(name);
if (at <= commandMatcher.end(1)) { if (at <= preprocessedCode.getStartPosition() + commandMatcher.end(1)) {
// The cursor is somewhere in the command name, show help text for the command. // The cursor is somewhere in the command name, show help text for the command.
return command.renderHelp(); return command.renderHelp();
} else if (at < commandMatcher.start(2)) { } else if (at < preprocessedCode.getStartPosition() + commandMatcher.start(2)) {
// The cursor is in the whitespace between the command name and arguments, don't show anything. // The cursor is in the whitespace between the command name and arguments, don't show anything.
return null; return null;
} else { } else {
...@@ -395,9 +417,9 @@ public final class ProBKernel extends BaseKernel { ...@@ -395,9 +417,9 @@ public final class ProBKernel extends BaseKernel {
assert name != null; assert name != null;
final PositionedString argString; final PositionedString argString;
if (commandMatcher.group(2) == null) { if (commandMatcher.group(2) == null) {
argString = code.substring(code.getValue().length()); argString = preprocessedCode.substring(preprocessedCode.getValue().length());
} else { } else {
argString = code.substring(commandMatcher.start(2), commandMatcher.end(2)); argString = preprocessedCode.substring(commandMatcher.start(2), commandMatcher.end(2));
} }
return inspectCommandArguments(command, argString, at); return inspectCommandArguments(command, argString, at);
} }
...@@ -405,10 +427,6 @@ public final class ProBKernel extends BaseKernel { ...@@ -405,10 +427,6 @@ public final class ProBKernel extends BaseKernel {
// Invalid command, can't inspect. // Invalid command, can't inspect.
return null; return null;
} }
} else {
// The code is not a valid command, ask :eval to inspect.
return inspectCommandArguments(this.getCommands().get(":eval"), code, at);
}
} }
@Override @Override
...@@ -444,18 +462,20 @@ public final class ProBKernel extends BaseKernel { ...@@ -444,18 +462,20 @@ public final class ProBKernel extends BaseKernel {
} }
private @Nullable ReplacementOptions completeInternal(final @NotNull PositionedString code, final int at) { private @Nullable ReplacementOptions completeInternal(final @NotNull PositionedString code, final int at) {
final Matcher commandMatcher = COMMAND_PATTERN.matcher(code.getValue()); final PositionedString preprocessedCode = preprocessInput(code);
if (commandMatcher.matches()) { final Matcher commandMatcher = COMMAND_PATTERN.matcher(preprocessedCode.getValue());
// The code is a valid command. if (!commandMatcher.matches()) {
if (at <= commandMatcher.end(1)) { throw new AssertionError("Preprocessed input does not include a command - this should not happen");
}
if (at <= preprocessedCode.getStartPosition() + commandMatcher.end(1)) {
// The cursor is somewhere in the command name, provide command completions. // The cursor is somewhere in the command name, provide command completions.
final String prefix = code.substring(commandMatcher.start(1), at).getValue(); final String prefix = preprocessedCode.substring(commandMatcher.start(1), at).getValue();
return new ReplacementOptions( return new ReplacementOptions(
this.getCommands().keySet().stream().filter(s -> s.startsWith(prefix)).sorted().collect(Collectors.toList()), this.getCommands().keySet().stream().filter(s -> s.startsWith(prefix)).sorted().collect(Collectors.toList()),
commandMatcher.start(1), commandMatcher.start(1),
commandMatcher.end(1) commandMatcher.end(1)
); );
} else if (at < commandMatcher.start(2)) { } else if (at < preprocessedCode.getStartPosition() + commandMatcher.start(2)) {
// The cursor is in the whitespace between the command name and arguments, don't show anything. // The cursor is in the whitespace between the command name and arguments, don't show anything.
return null; return null;
} else { } else {
...@@ -464,9 +484,9 @@ public final class ProBKernel extends BaseKernel { ...@@ -464,9 +484,9 @@ public final class ProBKernel extends BaseKernel {
assert name != null; assert name != null;
final PositionedString argString; final PositionedString argString;
if (commandMatcher.group(2) == null) { if (commandMatcher.group(2) == null) {
argString = code.substring(code.getValue().length()); argString = preprocessedCode.substring(preprocessedCode.getValue().length());
} else { } else {
argString = code.substring(commandMatcher.start(2), commandMatcher.end(2)); argString = preprocessedCode.substring(commandMatcher.start(2), commandMatcher.end(2));
} }
if (this.getCommands().containsKey(name)) { if (this.getCommands().containsKey(name)) {
return completeCommandArguments(this.getCommands().get(name), argString, at); return completeCommandArguments(this.getCommands().get(name), argString, at);
...@@ -475,10 +495,6 @@ public final class ProBKernel extends BaseKernel { ...@@ -475,10 +495,6 @@ public final class ProBKernel extends BaseKernel {
return null; return null;
} }
} }
} else {
// The code is not a valid command, ask :eval for completions.
return completeCommandArguments(this.getCommands().get(":eval"), code, at);
}
} }
@Override @Override
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment