mirror of
https://github.com/SpencerPark/IJava.git
synced 2025-04-16 03:16:21 +00:00
Simple isComplete support for terminal frontends including a naive auto indenter. Closes #35
This commit is contained in:
parent
2ad58cadc9
commit
992ed21fe5
@ -96,7 +96,7 @@ public class IJava {
|
||||
|
||||
String contents = new String(Files.readAllBytes(connectionFile));
|
||||
|
||||
JupyterSocket.JUPYTER_LOGGER.setLevel(Level.ALL);
|
||||
JupyterSocket.JUPYTER_LOGGER.setLevel(Level.WARNING);
|
||||
|
||||
KernelConnectionProperties connProps = KernelConnectionProperties.parse(contents);
|
||||
JupyterConnection connection = new JupyterConnection(connProps);
|
||||
|
@ -34,6 +34,9 @@ import io.github.spencerpark.jupyter.kernel.util.GlobFinder;
|
||||
import io.github.spencerpark.jupyter.kernel.util.StringStyler;
|
||||
import io.github.spencerpark.jupyter.kernel.util.TextColor;
|
||||
import io.github.spencerpark.jupyter.messages.Header;
|
||||
import io.github.spencerpark.jupyter.messages.publish.PublishError;
|
||||
import io.github.spencerpark.jupyter.messages.reply.ErrorReply;
|
||||
import io.github.spencerpark.jupyter.messages.reply.ExecuteReply;
|
||||
import jdk.jshell.*;
|
||||
|
||||
import java.io.IOException;
|
||||
@ -44,6 +47,18 @@ import java.util.stream.Collectors;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
public class JavaKernel extends BaseKernel {
|
||||
public static String completeCodeSignifier() {
|
||||
return BaseKernel.IS_COMPLETE_YES;
|
||||
}
|
||||
|
||||
public static String invalidCodeSignifier() {
|
||||
return BaseKernel.IS_COMPLETE_BAD;
|
||||
}
|
||||
|
||||
public static String maybeCompleteCodeSignifier() {
|
||||
return BaseKernel.IS_COMPLETE_MAYBE;
|
||||
}
|
||||
|
||||
private static final CharPredicate IDENTIFIER_CHAR = CharPredicate.builder()
|
||||
.inRange('a', 'z')
|
||||
.inRange('A', 'Z')
|
||||
@ -259,13 +274,21 @@ public class JavaKernel extends BaseKernel {
|
||||
|
||||
@Override
|
||||
public DisplayData eval(String expr) throws Exception {
|
||||
Object result = this.evalRaw(expr);
|
||||
try {
|
||||
Object result = this.evalRaw(expr);
|
||||
|
||||
if (result != null)
|
||||
return result instanceof DisplayData
|
||||
? (DisplayData) result
|
||||
: this.getRenderer().render(result);
|
||||
if (result != null)
|
||||
return result instanceof DisplayData
|
||||
? (DisplayData) result
|
||||
: this.getRenderer().render(result);
|
||||
|
||||
} catch (Exception e) {
|
||||
ErrorReply error = ErrorReply.of(e);
|
||||
PublishError pubError = PublishError.of(e, this::formatError);
|
||||
System.out.println(error);
|
||||
System.out.println(pubError);
|
||||
throw e;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@ -337,7 +360,7 @@ public class JavaKernel extends BaseKernel {
|
||||
|
||||
@Override
|
||||
public String isComplete(String code) {
|
||||
return super.isComplete(code);
|
||||
return this.evaluator.isComplete(code);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -23,11 +23,17 @@
|
||||
*/
|
||||
package io.github.spencerpark.ijava.execution;
|
||||
|
||||
import io.github.spencerpark.ijava.JavaKernel;
|
||||
import jdk.jshell.*;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class CodeEvaluator {
|
||||
private static final Pattern WHITESPACE_PREFIX = Pattern.compile("(?:^|\r?\n)(?<ws>\\s*).*$");
|
||||
private static final Pattern LAST_LINE = Pattern.compile("(?:^|\r?\n)(?<last>.*)$");
|
||||
|
||||
private static final String NO_MAGIC_RETURN = "\"__NO_MAGIC_RETURN\"";
|
||||
|
||||
private final JShell shell;
|
||||
@ -38,6 +44,8 @@ public class CodeEvaluator {
|
||||
private boolean isInitialized = false;
|
||||
private final List<String> startupScripts;
|
||||
|
||||
private final String indentation = " ";
|
||||
|
||||
public CodeEvaluator(JShell shell, IJavaExecutionControlProvider executionControlProvider, String executionControlID, List<String> startupScripts) {
|
||||
this.shell = shell;
|
||||
this.executionControlProvider = executionControlProvider;
|
||||
@ -144,6 +152,74 @@ public class CodeEvaluator {
|
||||
return lastEvalResult;
|
||||
}
|
||||
|
||||
private String computeIndentation(String partialStatement) {
|
||||
// Find the indentation of the last line
|
||||
Matcher m = WHITESPACE_PREFIX.matcher(partialStatement);
|
||||
String currentIndentation = m.find() ? m.group("ws") : "";
|
||||
|
||||
m = LAST_LINE.matcher(partialStatement);
|
||||
if (!m.find())
|
||||
throw new Error("Pattern broken. Every string should have a last line.");
|
||||
|
||||
// If a brace or paren was opened on the last line and not closed, indent some more.
|
||||
String lastLine = m.group("last");
|
||||
int newlyOpenedBraces = -1;
|
||||
int newlyOpenedParens = -1;
|
||||
for (int i = 0; i < lastLine.length(); i++) {
|
||||
switch (lastLine.charAt(i)) {
|
||||
case '}':
|
||||
// Ignore closing if one has not been opened on this line yet
|
||||
if (newlyOpenedBraces == -1) continue;
|
||||
// Otherwise close an opened one from this line
|
||||
newlyOpenedBraces--;
|
||||
break;
|
||||
case ')':
|
||||
// Same as for braces, but with the parens
|
||||
if (newlyOpenedParens == -1) continue;
|
||||
newlyOpenedParens--;
|
||||
break;
|
||||
case '{':
|
||||
// A brace was opened on this line!
|
||||
// If the first then get out og the -1 special case with an extra addition
|
||||
if (newlyOpenedBraces == -1) newlyOpenedBraces++;
|
||||
newlyOpenedBraces++;
|
||||
break;
|
||||
case '(':
|
||||
if (newlyOpenedParens == -1) newlyOpenedParens++;
|
||||
newlyOpenedParens++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return newlyOpenedBraces > 0 || newlyOpenedParens > 0
|
||||
? currentIndentation + this.indentation
|
||||
: currentIndentation;
|
||||
}
|
||||
|
||||
public String isComplete(String code) {
|
||||
SourceCodeAnalysis.CompletionInfo info = this.sourceAnalyzer.analyzeCompletion(code);
|
||||
while (info.completeness().isComplete())
|
||||
info = analyzeCompletion(info.remaining());
|
||||
|
||||
switch (info.completeness()) {
|
||||
case UNKNOWN:
|
||||
// Unknown means "bad code" and the only way to see if is complete is
|
||||
// to execute it.
|
||||
return JavaKernel.invalidCodeSignifier();
|
||||
case COMPLETE:
|
||||
case COMPLETE_WITH_SEMI:
|
||||
case EMPTY:
|
||||
return JavaKernel.completeCodeSignifier();
|
||||
case CONSIDERED_INCOMPLETE:
|
||||
case DEFINITELY_INCOMPLETE:
|
||||
// Compute the indent of the last line and match it
|
||||
return this.computeIndentation(info.remaining());
|
||||
default:
|
||||
// For completeness, return an "I don't know" if we somehow get down here
|
||||
return JavaKernel.maybeCompleteCodeSignifier();
|
||||
}
|
||||
}
|
||||
|
||||
public void interrupt() {
|
||||
IJavaExecutionControl executionControl =
|
||||
this.executionControlProvider.getRegisteredControlByID(this.executionControlID);
|
||||
|
Loading…
x
Reference in New Issue
Block a user