[cli-repl] Create wrapper for console reader

This commit is contained in:
Dmitry Kovanikov
2015-09-18 13:57:54 +03:00
committed by Pavel V. Talanov
parent 4aa5a90370
commit 18304c7d7b
5 changed files with 104 additions and 50 deletions
@@ -18,21 +18,19 @@ package org.jetbrains.kotlin.cli.jvm.repl;
import com.intellij.openapi.Disposable; import com.intellij.openapi.Disposable;
import com.intellij.openapi.util.io.FileUtil; import com.intellij.openapi.util.io.FileUtil;
import com.intellij.openapi.vfs.VirtualFile;
import com.intellij.testFramework.LightVirtualFile;
import jline.console.ConsoleReader;
import jline.console.history.FileHistory;
import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.jetbrains.kotlin.cli.common.KotlinVersion; import org.jetbrains.kotlin.cli.common.KotlinVersion;
import org.jetbrains.kotlin.cli.jvm.repl.messages.IdeLinebreaksUnescaper;
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplErrorLogger; import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplErrorLogger;
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplSystemInWrapper; import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplSystemInWrapper;
import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplSystemOutWrapper; import org.jetbrains.kotlin.cli.jvm.repl.messages.ReplSystemOutWrapper;
import org.jetbrains.kotlin.cli.jvm.repl.reader.ConsoleReplCommandReader;
import org.jetbrains.kotlin.cli.jvm.repl.reader.IdeReplCommandReader;
import org.jetbrains.kotlin.cli.jvm.repl.reader.ReplCommandReader;
import org.jetbrains.kotlin.config.CompilerConfiguration; import org.jetbrains.kotlin.config.CompilerConfiguration;
import org.jetbrains.kotlin.utils.UtilsPackage; import org.jetbrains.kotlin.utils.UtilsPackage;
import java.io.*; import java.io.File;
import java.io.PrintWriter;
import java.util.Arrays; import java.util.Arrays;
import java.util.List; import java.util.List;
@@ -47,7 +45,7 @@ public class ReplFromTerminal {
private final ReplSystemOutWrapper replWriter; private final ReplSystemOutWrapper replWriter;
private final ReplErrorLogger replErrorLogger; private final ReplErrorLogger replErrorLogger;
private ConsoleReader consoleReader; private ReplCommandReader commandReader;
public ReplFromTerminal( public ReplFromTerminal(
@NotNull final Disposable disposable, @NotNull final Disposable disposable,
@@ -87,24 +85,19 @@ public class ReplFromTerminal {
}.start(); }.start();
try { try {
OutputStream outStream = System.out; commandReader = createCommandReader();
if (ideMode) {
// consoleReader duplicates his input in his output stream;
// to prevent such behaviour we redirect his output to dummy output stream
VirtualFile consoleOutputRedirect = new LightVirtualFile("kotlinConsoleReplRedirect");
outStream = consoleOutputRedirect.getOutputStream(null);
}
consoleReader = new ConsoleReader("kotlin", System.in, outStream, null);
consoleReader.setHistoryEnabled(true);
consoleReader.setExpandEvents(false);
consoleReader.setHistory(new FileHistory(new File(new File(System.getProperty("user.home")), ".kotlin_history")));
} }
catch (Exception e) { catch (Exception e) {
replErrorLogger.logException(e); replErrorLogger.logException(e);
} }
} }
@NotNull
private ReplCommandReader createCommandReader() {
return ideMode ? new IdeReplCommandReader()
: new ConsoleReplCommandReader();
}
private ReplInterpreter getReplInterpreter() { private ReplInterpreter getReplInterpreter() {
if (replInterpreter != null) { if (replInterpreter != null) {
return replInterpreter; return replInterpreter;
@@ -143,7 +136,9 @@ public class ReplFromTerminal {
} }
finally { finally {
try { try {
((FileHistory) consoleReader.getHistory()).flush(); if (commandReader instanceof ConsoleReplCommandReader) {
((ConsoleReplCommandReader) commandReader).getFileHistory().flush();
}
} }
catch (Exception e) { catch (Exception e) {
replErrorLogger.logException(e); replErrorLogger.logException(e);
@@ -160,8 +155,8 @@ public class ReplFromTerminal {
@NotNull @NotNull
private WhatNextAfterOneLine one(@NotNull WhatNextAfterOneLine next) { private WhatNextAfterOneLine one(@NotNull WhatNextAfterOneLine next) {
try { try {
String prompt = getPrompt(next); String prompt = next == WhatNextAfterOneLine.INCOMPLETE ? "... " : ">>> ";
String line = readLine(prompt); String line = commandReader.readLine(prompt);
if (line == null) { if (line == null) {
return WhatNextAfterOneLine.QUIT; return WhatNextAfterOneLine.QUIT;
@@ -185,25 +180,6 @@ public class ReplFromTerminal {
} }
} }
@Nullable
private String getPrompt(@NotNull WhatNextAfterOneLine next) {
String prompt = null;
// consoleReader always print prompt; in [ideMode] we don't allow it
if (!ideMode) {
prompt = next == WhatNextAfterOneLine.INCOMPLETE ? "... " : ">>> ";
}
return prompt;
}
@Nullable
private String readLine(@Nullable String prompt) throws IOException {
String line = consoleReader.readLine(prompt);
if (ideMode && line != null) {
line = IdeLinebreaksUnescaper.unescapeFromDiez(line).trim();
}
return line;
}
@NotNull @NotNull
private ReplInterpreter.LineResultType eval(@NotNull String line) { private ReplInterpreter.LineResultType eval(@NotNull String line) {
ReplInterpreter.LineResult lineResult = getReplInterpreter().eval(line); ReplInterpreter.LineResult lineResult = getReplInterpreter().eval(line);
@@ -24,7 +24,7 @@ public class ReplSystemInWrapper(
private val replWriter: ReplSystemOutWrapper private val replWriter: ReplSystemOutWrapper
) : InputStream() { ) : InputStream() {
private var isXmlIncomplete = true private var isXmlIncomplete = true
private var isLastScriptByteProcessed = false private var isLastByteProcessed = false
private var isReadLineStartSent = false private var isReadLineStartSent = false
private var byteBuilder = ByteArrayOutputStream() private var byteBuilder = ByteArrayOutputStream()
private var curBytePos = 0 private var curBytePos = 0
@@ -36,10 +36,13 @@ public class ReplSystemInWrapper(
var isReplScriptExecuting = false var isReplScriptExecuting = false
override fun read(): Int { override fun read(): Int {
if (isLastScriptByteProcessed && isReplScriptExecuting) { if (isLastByteProcessed) {
isLastScriptByteProcessed = false if (isReplScriptExecuting) {
isReadLineStartSent = false isReadLineStartSent = false
replWriter.printlnReadLineEnd() replWriter.printlnReadLineEnd()
}
isLastByteProcessed = false
return -1 return -1
} }
@@ -53,7 +56,7 @@ public class ReplSystemInWrapper(
if (byteBuilder.toString().endsWith(END_LINE)) { if (byteBuilder.toString().endsWith(END_LINE)) {
isXmlIncomplete = false isXmlIncomplete = false
isLastScriptByteProcessed = false isLastByteProcessed = false
inputByteArray = unescapedInput().toByteArray() inputByteArray = unescapedInput().toByteArray()
} }
@@ -81,8 +84,7 @@ public class ReplSystemInWrapper(
isXmlIncomplete = true isXmlIncomplete = true
byteBuilder = ByteArrayOutputStream() byteBuilder = ByteArrayOutputStream()
curBytePos = 0 curBytePos = 0
isLastByteProcessed = true
if (isReplScriptExecuting) isLastScriptByteProcessed = true
} }
} }
} }
@@ -0,0 +1,34 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.reader
import jline.console.ConsoleReader
import jline.console.history.FileHistory
import java.io.File
public class ConsoleReplCommandReader : ReplCommandReader {
private val consoleReader = ConsoleReader("kotlin", System.`in`, System.`out`, null) apply {
isHistoryEnabled = true
expandEvents = false
history = FileHistory(File(File(System.getProperty("user.home")), ".kotlin_history"))
}
public val fileHistory: FileHistory
get() = consoleReader.history as FileHistory
override fun readLine(prompt: String?) = consoleReader.readLine(prompt)
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.reader
public class IdeReplCommandReader : ReplCommandReader {
override fun readLine(prompt: String?) = readLine()
}
@@ -0,0 +1,21 @@
/*
* Copyright 2010-2015 JetBrains s.r.o.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.jetbrains.kotlin.cli.jvm.repl.reader
public interface ReplCommandReader {
public fun readLine(prompt: String?): String?
}