Add kotlin console repl
This commit is contained in:
committed by
Pavel V. Talanov
parent
80cbee83ee
commit
a11f411f7d
Generated
+1
@@ -46,6 +46,7 @@
|
||||
<element id="module-output" name="container" />
|
||||
<element id="module-output" name="rmi-interface" />
|
||||
<element id="module-output" name="kotlinr" />
|
||||
<element id="module-output" name="idea-repl" />
|
||||
</element>
|
||||
<element id="library" level="project" name="javax.inject" />
|
||||
<element id="directory" name="jps">
|
||||
|
||||
Generated
+1
@@ -36,6 +36,7 @@
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-core/idea-core.iml" filepath="$PROJECT_DIR$/idea/idea-core/idea-core.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-jps-common/idea-jps-common.iml" filepath="$PROJECT_DIR$/idea/idea-jps-common/idea-jps-common.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-js/idea-js.iml" filepath="$PROJECT_DIR$/idea/idea-js/idea-js.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-repl/idea-repl.iml" filepath="$PROJECT_DIR$/idea/idea-repl/idea-repl.iml" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea-runner/idea-runner.iml" filepath="$PROJECT_DIR$/idea-runner/idea-runner.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/idea/idea-test-framework/idea-test-framework.iml" filepath="$PROJECT_DIR$/idea/idea-test-framework/idea-test-framework.iml" group="ide" />
|
||||
<module fileurl="file://$PROJECT_DIR$/compiler/preloader/instrumentation/instrumentation.iml" filepath="$PROJECT_DIR$/compiler/preloader/instrumentation/instrumentation.iml" group="compiler/cli" />
|
||||
|
||||
+1
-1
@@ -47,4 +47,4 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
</module>
|
||||
+30
@@ -242,6 +242,36 @@ public final class AnalyzerWithCompilerReport {
|
||||
return new SyntaxErrorReport(visitor.hasErrors, visitor.allErrorsAtEof);
|
||||
}
|
||||
|
||||
public static SyntaxErrorReport reportSyntaxErrorsWithDiagnostics(@NotNull final PsiElement file, @NotNull final MessageCollector messageCollector,
|
||||
@NotNull final List<Diagnostic> diagnostics) {
|
||||
class ErrorReportingVisitor extends AnalyzingUtils.PsiErrorElementVisitor {
|
||||
boolean hasErrors = false;
|
||||
boolean allErrorsAtEof = true;
|
||||
|
||||
private <E extends PsiElement> void reportDiagnostic(E element, DiagnosticFactory0<E> factory, String message) {
|
||||
MyDiagnostic<?> diagnostic = new MyDiagnostic<E>(element, factory, message);
|
||||
AnalyzerWithCompilerReport.reportDiagnostic(diagnostic, messageCollector);
|
||||
if (element.getTextRange().getStartOffset() != file.getTextRange().getEndOffset()) {
|
||||
allErrorsAtEof = false;
|
||||
}
|
||||
hasErrors = true;
|
||||
|
||||
diagnostics.add(diagnostic);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void visitErrorElement(PsiErrorElement element) {
|
||||
String description = element.getErrorDescription();
|
||||
reportDiagnostic(element, SYNTAX_ERROR_FACTORY, StringUtil.isEmpty(description) ? "Syntax error" : description);
|
||||
}
|
||||
}
|
||||
ErrorReportingVisitor visitor = new ErrorReportingVisitor();
|
||||
|
||||
file.accept(visitor);
|
||||
|
||||
return new SyntaxErrorReport(visitor.hasErrors, visitor.allErrorsAtEof);
|
||||
}
|
||||
|
||||
@Nullable
|
||||
public AnalysisResult getAnalysisResult() {
|
||||
return analysisResult;
|
||||
|
||||
@@ -18,6 +18,8 @@ package org.jetbrains.kotlin.cli.jvm.repl;
|
||||
|
||||
import com.intellij.openapi.Disposable;
|
||||
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;
|
||||
@@ -26,6 +28,7 @@ import org.jetbrains.kotlin.config.CompilerConfiguration;
|
||||
import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.OutputStream;
|
||||
import java.io.PrintWriter;
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
@@ -36,6 +39,9 @@ public class ReplFromTerminal {
|
||||
private Throwable replInitializationFailed;
|
||||
private final Object waitRepl = new Object();
|
||||
|
||||
private final boolean ideMode;
|
||||
private ReplSystemOutWrapper replWriter;
|
||||
|
||||
private final ConsoleReader consoleReader;
|
||||
|
||||
public ReplFromTerminal(
|
||||
@@ -56,8 +62,24 @@ public class ReplFromTerminal {
|
||||
}
|
||||
}.start();
|
||||
|
||||
String replIdeMode = System.getProperty("repl.ideMode");
|
||||
ideMode = replIdeMode != null && replIdeMode.equals("true");
|
||||
|
||||
try {
|
||||
consoleReader = new ConsoleReader("kotlin", System.in, System.out, null);
|
||||
OutputStream outStream = System.out;
|
||||
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);
|
||||
|
||||
// wrapper is required to escape every input;
|
||||
// if user calls [System.setOut(...)] then undefined behaviour
|
||||
replWriter = new ReplSystemOutWrapper(System.out);
|
||||
System.setOut(replWriter);
|
||||
}
|
||||
|
||||
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")));
|
||||
@@ -65,6 +87,8 @@ public class ReplFromTerminal {
|
||||
catch (Exception e) {
|
||||
throw UtilsPackage.rethrow(e);
|
||||
}
|
||||
|
||||
getReplInterpreter().setIdeMode(ideMode);
|
||||
}
|
||||
|
||||
private ReplInterpreter getReplInterpreter() {
|
||||
@@ -122,7 +146,9 @@ public class ReplFromTerminal {
|
||||
@NotNull
|
||||
private WhatNextAfterOneLine one(@NotNull WhatNextAfterOneLine next) {
|
||||
try {
|
||||
String line = consoleReader.readLine(next == WhatNextAfterOneLine.INCOMPLETE ? "... " : ">>> ");
|
||||
String prompt = getPrompt(next);
|
||||
String line = consoleReader.readLine(prompt);
|
||||
|
||||
if (line == null) {
|
||||
return WhatNextAfterOneLine.QUIT;
|
||||
}
|
||||
@@ -145,6 +171,15 @@ public class ReplFromTerminal {
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private ReplInterpreter.LineResultType eval(@NotNull String line) {
|
||||
ReplInterpreter.LineResult lineResult = getReplInterpreter().eval(line);
|
||||
@@ -156,7 +191,11 @@ public class ReplFromTerminal {
|
||||
else if (lineResult.getType() == ReplInterpreter.LineResultType.INCOMPLETE) {
|
||||
}
|
||||
else if (lineResult.getType() == ReplInterpreter.LineResultType.ERROR) {
|
||||
System.out.print(lineResult.getErrorText());
|
||||
if (ideMode) {
|
||||
replWriter.printlnWithEscaping(lineResult.getErrorText(), EscapeType.REPORT);
|
||||
} else {
|
||||
System.out.print(lineResult.getErrorText());
|
||||
}
|
||||
}
|
||||
else {
|
||||
throw new IllegalStateException("unknown line result type: " + lineResult);
|
||||
|
||||
@@ -21,6 +21,8 @@ import com.google.common.collect.Lists;
|
||||
import com.intellij.openapi.Disposable;
|
||||
import com.intellij.openapi.project.Project;
|
||||
import com.intellij.openapi.util.Pair;
|
||||
import com.intellij.openapi.util.TextRange;
|
||||
import com.intellij.openapi.util.text.StringUtil;
|
||||
import com.intellij.openapi.vfs.CharsetToolkit;
|
||||
import com.intellij.psi.PsiElement;
|
||||
import com.intellij.psi.PsiFileFactory;
|
||||
@@ -47,6 +49,7 @@ import org.jetbrains.kotlin.context.MutableModuleContext;
|
||||
import org.jetbrains.kotlin.descriptors.ScriptDescriptor;
|
||||
import org.jetbrains.kotlin.descriptors.impl.CompositePackageFragmentProvider;
|
||||
import org.jetbrains.kotlin.descriptors.impl.ModuleDescriptorImpl;
|
||||
import org.jetbrains.kotlin.diagnostics.Diagnostic;
|
||||
import org.jetbrains.kotlin.idea.JetLanguage;
|
||||
import org.jetbrains.kotlin.name.FqName;
|
||||
import org.jetbrains.kotlin.parsing.JetParserDefinition;
|
||||
@@ -65,18 +68,30 @@ import org.jetbrains.kotlin.resolve.scopes.JetScope;
|
||||
import org.jetbrains.kotlin.types.JetType;
|
||||
import org.jetbrains.kotlin.utils.UtilsPackage;
|
||||
import org.jetbrains.org.objectweb.asm.Type;
|
||||
import org.w3c.dom.Attr;
|
||||
import org.w3c.dom.Document;
|
||||
import org.w3c.dom.Element;
|
||||
import org.w3c.dom.ls.DOMImplementationLS;
|
||||
import org.w3c.dom.ls.LSSerializer;
|
||||
|
||||
import javax.xml.parsers.DocumentBuilder;
|
||||
import javax.xml.parsers.DocumentBuilderFactory;
|
||||
import javax.xml.parsers.ParserConfigurationException;
|
||||
import javax.xml.transform.Transformer;
|
||||
import javax.xml.transform.TransformerConfigurationException;
|
||||
import javax.xml.transform.TransformerException;
|
||||
import javax.xml.transform.TransformerFactory;
|
||||
import javax.xml.transform.dom.DOMSource;
|
||||
import javax.xml.transform.stream.StreamResult;
|
||||
import java.io.File;
|
||||
import java.io.PrintWriter;
|
||||
import java.io.StringWriter;
|
||||
import java.lang.reflect.Constructor;
|
||||
import java.lang.reflect.Field;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.net.URLClassLoader;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.*;
|
||||
|
||||
import static org.jetbrains.kotlin.cli.jvm.config.ConfigPackage.getJvmClasspathRoots;
|
||||
import static org.jetbrains.kotlin.cli.jvm.config.ConfigPackage.getModuleName;
|
||||
@@ -102,6 +117,8 @@ public class ReplInterpreter {
|
||||
private final ResolveSession resolveSession;
|
||||
private final ScriptMutableDeclarationProviderFactory scriptDeclarationFactory;
|
||||
|
||||
private boolean ideMode;
|
||||
|
||||
public ReplInterpreter(@NotNull Disposable disposable, @NotNull CompilerConfiguration configuration) {
|
||||
KotlinCoreEnvironment environment =
|
||||
KotlinCoreEnvironment.createForProduction(disposable, configuration, EnvironmentConfigFiles.JVM_CONFIG_FILES);
|
||||
@@ -159,12 +176,21 @@ public class ReplInterpreter {
|
||||
c.getScripts().clear();
|
||||
}
|
||||
|
||||
public void setIdeMode(boolean ideMode) {
|
||||
this.ideMode = ideMode;
|
||||
}
|
||||
|
||||
public enum LineResultType {
|
||||
SUCCESS,
|
||||
ERROR,
|
||||
INCOMPLETE,
|
||||
}
|
||||
|
||||
private enum ErrorType {
|
||||
CODE_ERROR,
|
||||
THROWABLE
|
||||
}
|
||||
|
||||
public static class LineResult {
|
||||
private final Object value;
|
||||
private final boolean unit;
|
||||
@@ -208,16 +234,77 @@ public class ReplInterpreter {
|
||||
return new LineResult(value, unit, null, LineResultType.SUCCESS);
|
||||
}
|
||||
|
||||
public static LineResult error(@NotNull String errorText) {
|
||||
public static LineResult error(@NotNull String errorText, boolean ideMode,
|
||||
@NotNull ErrorType errType, @Nullable Collection<Diagnostic> diagnostics
|
||||
) {
|
||||
if (errorText.isEmpty()) {
|
||||
errorText = "<unknown error>";
|
||||
}
|
||||
else if (!errorText.endsWith("\n")) {
|
||||
|
||||
String[] lines = errorTextToLinesWithPreprocessing(errorText);
|
||||
|
||||
if (ideMode && errType == ErrorType.CODE_ERROR) {
|
||||
assert diagnostics != null : "diagnostics should not be null in not-throwable case";
|
||||
errorText = convertLinesToXML(lines, diagnostics);
|
||||
}
|
||||
else {
|
||||
errorText = StringUtil.join(lines, "\n");
|
||||
}
|
||||
|
||||
if (!errorText.endsWith("\n")) {
|
||||
errorText += "\n";
|
||||
}
|
||||
|
||||
return new LineResult(null, false, errorText, LineResultType.ERROR);
|
||||
}
|
||||
|
||||
private static String[] errorTextToLinesWithPreprocessing(@NotNull String errorText) {
|
||||
// cut "/line_i.kts:row:column" prefix and capitalize "error:" and "warning:"
|
||||
String[] lines = errorText.split("\\n");
|
||||
for (int i = 0; i < lines.length; i++) {
|
||||
if (lines[i].contains("error:")) {
|
||||
int pos = lines[i].indexOf("error:");
|
||||
lines[i] = "E" + lines[i].substring(pos + 1);
|
||||
}
|
||||
else if (lines[i].contains("warning:")) {
|
||||
int pos = lines[i].indexOf("warning:");
|
||||
lines[i] = "W" + lines[i].substring(pos + 1);
|
||||
}
|
||||
}
|
||||
return lines;
|
||||
}
|
||||
|
||||
private static String convertLinesToXML(@NotNull String[] lines, @NotNull Collection<Diagnostic> diagnostics) {
|
||||
try {
|
||||
DocumentBuilderFactory docFactory = DocumentBuilderFactory.newInstance();
|
||||
DocumentBuilder docBuilder = docFactory.newDocumentBuilder();
|
||||
Document errorReport = docBuilder.newDocument();
|
||||
|
||||
Element rootElement = errorReport.createElement("report");
|
||||
errorReport.appendChild(rootElement);
|
||||
|
||||
Iterator<Diagnostic> di = diagnostics.iterator();
|
||||
for (String s : lines) {
|
||||
if (s.startsWith("Error:") || s.startsWith("Warning:")) {
|
||||
TextRange errorRange = di.hasNext() ? di.next().getTextRanges().get(0) : TextRange.EMPTY_RANGE;
|
||||
|
||||
Element reportEntry = errorReport.createElement("reportEntry");
|
||||
reportEntry.setAttribute("rangeStart", String.valueOf(errorRange.getStartOffset()));
|
||||
reportEntry.setAttribute("rangeEnd", String.valueOf(errorRange.getEndOffset()));
|
||||
reportEntry.appendChild(errorReport.createTextNode(StringUtil.escapeXml(s)));
|
||||
|
||||
rootElement.appendChild(reportEntry);
|
||||
}
|
||||
}
|
||||
|
||||
DOMImplementationLS domImplementation = (DOMImplementationLS) errorReport.getImplementation();
|
||||
LSSerializer lsSerializer = domImplementation.createLSSerializer();
|
||||
return lsSerializer.writeToString(errorReport);
|
||||
} catch (ParserConfigurationException e) {
|
||||
throw UtilsPackage.rethrow(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static LineResult incomplete() {
|
||||
return new LineResult(null, false, null, LineResultType.INCOMPLETE);
|
||||
}
|
||||
@@ -242,9 +329,10 @@ public class ReplInterpreter {
|
||||
assert psiFile != null : "Script file not analyzed at line " + lineNumber + ": " + fullText;
|
||||
|
||||
ReplMessageCollectorWrapper errorCollector = new ReplMessageCollectorWrapper();
|
||||
List<Diagnostic> syntaxDiagnostics = new ArrayList<Diagnostic>();
|
||||
|
||||
AnalyzerWithCompilerReport.SyntaxErrorReport syntaxErrorReport =
|
||||
AnalyzerWithCompilerReport.reportSyntaxErrors(psiFile, errorCollector.getMessageCollector());
|
||||
AnalyzerWithCompilerReport.reportSyntaxErrorsWithDiagnostics(psiFile, errorCollector.getMessageCollector(), syntaxDiagnostics);
|
||||
|
||||
if (syntaxErrorReport.isHasErrors() && syntaxErrorReport.isAllErrorsAtEof()) {
|
||||
previousIncompleteLines.add(line);
|
||||
@@ -254,7 +342,7 @@ public class ReplInterpreter {
|
||||
previousIncompleteLines.clear();
|
||||
|
||||
if (syntaxErrorReport.isHasErrors()) {
|
||||
return LineResult.error(errorCollector.getString());
|
||||
return LineResult.error(errorCollector.getString(), ideMode, ErrorType.CODE_ERROR, syntaxDiagnostics);
|
||||
}
|
||||
|
||||
prepareForTheNextReplLine(topDownAnalysisContext);
|
||||
@@ -265,7 +353,7 @@ public class ReplInterpreter {
|
||||
|
||||
ScriptDescriptor scriptDescriptor = doAnalyze(psiFile, errorCollector);
|
||||
if (scriptDescriptor == null) {
|
||||
return LineResult.error(errorCollector.getString());
|
||||
return LineResult.error(errorCollector.getString(), ideMode, ErrorType.CODE_ERROR, trace.getBindingContext().getDiagnostics().all());
|
||||
}
|
||||
|
||||
List<Pair<ScriptDescriptor, Type>> earlierScripts = Lists.newArrayList();
|
||||
@@ -303,7 +391,7 @@ public class ReplInterpreter {
|
||||
scriptInstance = scriptInstanceConstructor.newInstance(constructorArgs);
|
||||
}
|
||||
catch (Throwable e) {
|
||||
return LineResult.error(renderStackTrace(e.getCause()));
|
||||
return LineResult.error(renderStackTrace(e.getCause()), ideMode, ErrorType.THROWABLE, null);
|
||||
}
|
||||
Field rvField = scriptClass.getDeclaredField("rv");
|
||||
rvField.setAccessible(true);
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
/*
|
||||
* 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
|
||||
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import java.io.PrintStream
|
||||
|
||||
enum class EscapeType {
|
||||
ORDINARY, REPORT
|
||||
}
|
||||
|
||||
public class ReplSystemOutWrapper(private val standardOut: PrintStream) : PrintStream(standardOut, true) {
|
||||
private val XML_PREFIX = "<?xml version=\"1.0\" encoding=\"UTF-16\"?>"
|
||||
|
||||
private fun xmlEscape(s: String, escapeType: EscapeType): String {
|
||||
val singleLine = StringUtil.escapeLineBreak(s)
|
||||
return "$XML_PREFIX<output type=\"$escapeType\">${StringUtil.escapeXml(singleLine)}</output>"
|
||||
}
|
||||
|
||||
override fun print(x: Any) = printWithEscaping(x.toString())
|
||||
private fun printWithEscaping(text: String, escapeType: EscapeType = EscapeType.ORDINARY) = super.print(xmlEscape(text, escapeType))
|
||||
|
||||
override fun println(x: String) = printlnWithEscaping(x)
|
||||
override fun println(x: Any) = printlnWithEscaping(x.toString())
|
||||
public fun printlnWithEscaping(text: String, escapeType: EscapeType = EscapeType.ORDINARY): Unit = super.println(xmlEscape(text, escapeType))
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="JAVA_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="library" name="idea-full" level="project" />
|
||||
<orderEntry type="module" module-name="frontend" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
||||
@@ -0,0 +1,108 @@
|
||||
/*
|
||||
* 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.console
|
||||
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.configurations.JavaParameters
|
||||
import com.intellij.openapi.components.ServiceManager
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.JavaSdkType
|
||||
import com.intellij.openapi.projectRoots.JdkUtil
|
||||
import com.intellij.openapi.projectRoots.SimpleJavaSdkType
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.SystemProperties
|
||||
import org.jetbrains.kotlin.console.actions.errorNotification
|
||||
import org.jetbrains.kotlin.utils.PathUtil
|
||||
import java.io.File
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import kotlin.platform.platformStatic
|
||||
|
||||
public class KotlinConsoleKeeper(val project: Project) {
|
||||
private val consoleMap: MutableMap<VirtualFile, KotlinConsoleRunner> = ConcurrentHashMap()
|
||||
|
||||
fun getConsoleByVirtualFile(virtualFile: VirtualFile) = consoleMap.get(virtualFile)
|
||||
fun putVirtualFileToConsole(virtualFile: VirtualFile, console: KotlinConsoleRunner) = consoleMap.put(virtualFile, console)
|
||||
fun removeConsole(virtualFile: VirtualFile?) = consoleMap.remove(virtualFile)
|
||||
|
||||
fun run(module: Module): KotlinConsoleRunner? {
|
||||
val path = module.moduleFilePath
|
||||
val cmdLine = createCommandLine(module)
|
||||
if (cmdLine == null) {
|
||||
errorNotification(project, "<p>Module SDK not found</p>")
|
||||
return null
|
||||
}
|
||||
|
||||
val consoleRunner = KotlinConsoleRunner(path, cmdLine, project, REPL_TITLE)
|
||||
consoleRunner.initAndRun()
|
||||
consoleRunner.setupGutters()
|
||||
|
||||
return consoleRunner
|
||||
}
|
||||
|
||||
private fun createCommandLine(module: Module): GeneralCommandLine? {
|
||||
val javaParameters = createJavaParametersWithSdk(module)
|
||||
val sdk = javaParameters.jdk ?: return null
|
||||
val sdkType = sdk.sdkType
|
||||
val exePath = (sdkType as JavaSdkType).getVMExecutablePath(sdk)
|
||||
|
||||
val commandLine = JdkUtil.setupJVMCommandLine(exePath, javaParameters, true)
|
||||
|
||||
// set parameters to run compiler
|
||||
val paramList = commandLine.parametersList
|
||||
paramList.clearAll()
|
||||
|
||||
// use to debug repl process
|
||||
//paramList.add("-agentlib:jdwp=transport=dt_socket,server=y,suspend=y,address=5005")
|
||||
|
||||
// set classpath for process to compiler
|
||||
paramList.add("-cp")
|
||||
paramList.add("${PathUtil.getKotlinPathsForIdeaPlugin().libPath.absolutePath}/*")
|
||||
|
||||
// set param to prevent process from repeating input backwards
|
||||
paramList.add("-Drepl.ideMode=true")
|
||||
|
||||
// path to compiler and his options
|
||||
paramList.add("org.jetbrains.kotlin.cli.jvm.K2JVMCompiler")
|
||||
|
||||
return commandLine
|
||||
}
|
||||
|
||||
private fun createJavaParametersWithSdk(module: Module?): JavaParameters {
|
||||
val params = JavaParameters()
|
||||
params.charset = null
|
||||
|
||||
if (module != null) {
|
||||
val sdk = ModuleRootManager.getInstance(module).sdk
|
||||
if (sdk != null && sdk.sdkType is JavaSdkType && File(sdk.homePath).exists()) {
|
||||
params.jdk = sdk
|
||||
}
|
||||
}
|
||||
if (params.jdk == null) {
|
||||
params.jdk = SimpleJavaSdkType().createJdk("tmp", SystemProperties.getJavaHome())
|
||||
}
|
||||
|
||||
return params
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val REPL_TITLE = "Kotlin REPL"
|
||||
|
||||
platformStatic fun getInstance(project: Project) = ServiceManager.getService(project, javaClass<KotlinConsoleKeeper>())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,163 @@
|
||||
/*
|
||||
* 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.console
|
||||
|
||||
import com.intellij.execution.Executor
|
||||
import com.intellij.execution.configurations.GeneralCommandLine
|
||||
import com.intellij.execution.console.*
|
||||
import com.intellij.execution.process.*
|
||||
import com.intellij.execution.runners.AbstractConsoleRunnerWithHistory
|
||||
import com.intellij.execution.ui.RunContentDescriptor
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.openapi.actionSystem.*
|
||||
import com.intellij.openapi.editor.colors.EditorColors
|
||||
import com.intellij.openapi.editor.event.DocumentAdapter
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.markup.*
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.console.actions.KtExecuteCommandAction
|
||||
import org.jetbrains.kotlin.console.actions.logError
|
||||
import org.jetbrains.kotlin.idea.JetLanguage
|
||||
import java.awt.Color
|
||||
import javax.swing.Icon
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public class KotlinConsoleRunner(
|
||||
val title: String,
|
||||
private val cmdLine: GeneralCommandLine,
|
||||
myProject: Project,
|
||||
path: String?
|
||||
) : AbstractConsoleRunnerWithHistory<LanguageConsoleView>(myProject, title, path) {
|
||||
companion object {
|
||||
private val HISTORY_GUTTER_ICON = AllIcons.Debugger.Console
|
||||
private val EDITOR_GUTTER_ICON = AllIcons.Debugger.CommandLine
|
||||
}
|
||||
|
||||
private val keyEventListener = KtConsoleKeyListener(this)
|
||||
private var historyHighlighter: KotlinReplResultHighlighter by Delegates.notNull()
|
||||
val history: MutableList<String> = arrayListOf()
|
||||
|
||||
override fun createProcess() = cmdLine.createProcess()
|
||||
|
||||
override fun createConsoleView(): LanguageConsoleView? {
|
||||
val consoleView = LanguageConsoleBuilder().build(project, JetLanguage.INSTANCE)
|
||||
consoleView.prompt = null
|
||||
|
||||
val historyEditor = consoleView.historyViewer
|
||||
val consoleEditor = consoleView.consoleEditor
|
||||
consoleEditor.contentComponent.addKeyListener(keyEventListener)
|
||||
|
||||
historyEditor.document.addDocumentListener(object : DocumentAdapter() {
|
||||
override fun documentChanged(e: DocumentEvent): Unit =
|
||||
if (historyEditor.document.textLength == 0) addGutterIcon(historyEditor, HISTORY_GUTTER_ICON)
|
||||
})
|
||||
|
||||
historyHighlighter = KotlinReplResultHighlighter(historyEditor)
|
||||
historyEditor.document.addDocumentListener(historyHighlighter)
|
||||
|
||||
consoleEditor.setPlaceholder("<Ctrl+Enter> to execute")
|
||||
consoleEditor.setShowPlaceholderWhenFocused(true)
|
||||
val placeholderAttrs = consoleEditor.foldingModel.placeholderAttributes
|
||||
placeholderAttrs.foregroundColor = Color.GRAY
|
||||
|
||||
val executeAction = KtExecuteCommandAction(consoleView.virtualFile)
|
||||
executeAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, consoleView.consoleEditor.component)
|
||||
|
||||
return consoleView
|
||||
}
|
||||
|
||||
override fun createProcessHandler(process: Process): OSProcessHandler {
|
||||
val processHandler = KotlinReplOutputHandler(process, cmdLine.commandLineString)
|
||||
historyHighlighter.rangeQueue = processHandler.rangeQueue
|
||||
val consoleFile = consoleView.virtualFile
|
||||
val keeper = KotlinConsoleKeeper.getInstance(project)
|
||||
|
||||
keeper.putVirtualFileToConsole(consoleFile, this)
|
||||
processHandler.addProcessListener(object : ProcessAdapter() {
|
||||
override fun processTerminated(event: ProcessEvent) {
|
||||
keeper.removeConsole(consoleFile)
|
||||
}
|
||||
})
|
||||
|
||||
return processHandler
|
||||
}
|
||||
|
||||
override fun createExecuteActionHandler() = object : ProcessBackedConsoleExecuteActionHandler(processHandler, false) {
|
||||
override fun sendText(line: String) {
|
||||
submitCommand(line)
|
||||
}
|
||||
}
|
||||
|
||||
override fun fillToolBarActions(toolbarActions: DefaultActionGroup,
|
||||
defaultExecutor: Executor,
|
||||
contentDescriptor: RunContentDescriptor
|
||||
): List<AnAction> {
|
||||
val actionList = arrayListOf<AnAction>(
|
||||
createCloseAction(defaultExecutor, contentDescriptor),
|
||||
createConsoleExecAction(consoleExecuteActionHandler)
|
||||
)
|
||||
toolbarActions.addAll(actionList)
|
||||
return actionList
|
||||
}
|
||||
|
||||
override fun createConsoleExecAction(consoleExecuteActionHandler: ProcessBackedConsoleExecuteActionHandler)
|
||||
= ConsoleExecuteAction(consoleView, consoleExecuteActionHandler, "KotlinShellExecute", consoleExecuteActionHandler)
|
||||
|
||||
fun setupGutters() {
|
||||
fun configureEditorGutter(editor: EditorEx, color: Color, icon: Icon) {
|
||||
editor.settings.isLineMarkerAreaShown = true // hack to show gutter
|
||||
editor.settings.isFoldingOutlineShown = true
|
||||
editor.gutterComponentEx.setPaintBackground(true)
|
||||
val editorColorScheme = editor.colorsScheme
|
||||
editorColorScheme.setColor(EditorColors.GUTTER_BACKGROUND, color)
|
||||
editor.colorsScheme = editorColorScheme
|
||||
|
||||
addGutterIcon(editor, icon)
|
||||
}
|
||||
|
||||
val consoleView = consoleView
|
||||
val lightGray = Color(0xF2, 0xF2, 0xF2)
|
||||
val lightBlue = Color(0x93, 0xDE, 0xFF)
|
||||
configureEditorGutter(consoleView.historyViewer, lightGray, HISTORY_GUTTER_ICON)
|
||||
configureEditorGutter(consoleView.consoleEditor, lightBlue, EDITOR_GUTTER_ICON)
|
||||
}
|
||||
|
||||
private fun addGutterIcon(editor: EditorEx, icon: Icon) {
|
||||
val editorMarkup = editor.markupModel
|
||||
val highlighter = editorMarkup.addRangeHighlighter(0, editor.document.textLength, HighlighterLayer.LAST, null, HighlighterTargetArea.LINES_IN_RANGE)
|
||||
highlighter.gutterIconRenderer = object : GutterIconRenderer() {
|
||||
override fun getIcon() = icon
|
||||
override fun hashCode() = System.identityHashCode(this)
|
||||
override fun equals(other: Any?) = this === other
|
||||
}
|
||||
}
|
||||
|
||||
public fun submitCommand(command: String) {
|
||||
val res = command.trim()
|
||||
if (res.isEmpty()) return
|
||||
|
||||
history.add(res)
|
||||
keyEventListener.resetHistoryPosition()
|
||||
|
||||
val processInputOS = processHandler.processInput ?: return logError(javaClass, "<p>Broken process stream</p>")
|
||||
val charset = (processHandler as? BaseOSProcessHandler)?.charset ?: Charsets.UTF_8
|
||||
val bytes = ("$res\n").toByteArray(charset)
|
||||
processInputOS.write(bytes)
|
||||
processInputOS.flush()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
/*
|
||||
* 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.console
|
||||
|
||||
import com.intellij.execution.process.OSProcessHandler
|
||||
import com.intellij.openapi.util.Key
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.w3c.dom.Element
|
||||
import org.xml.sax.InputSource
|
||||
import java.io.ByteArrayInputStream
|
||||
import java.nio.charset.Charset
|
||||
import java.util.*
|
||||
import java.util.concurrent.ConcurrentLinkedQueue
|
||||
import javax.xml.parsers.DocumentBuilderFactory
|
||||
|
||||
public class KotlinReplOutputHandler(
|
||||
process: Process,
|
||||
commandLine: String
|
||||
) : OSProcessHandler(process, commandLine) {
|
||||
private val dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
|
||||
val rangeQueue: Queue<TextRange> = ConcurrentLinkedQueue()
|
||||
|
||||
override fun notifyTextAvailable(text: String, key: Key<*>?) {
|
||||
// skip "/usr/lib/jvm/java-8-oracle/bin/java -cp ..." intro
|
||||
if (!text.startsWith("<?xml version=")) return super.notifyTextAvailable(text, key)
|
||||
|
||||
val output = dBuilder.parse(strToSource(text))
|
||||
val root = output.firstChild as Element
|
||||
val outputType = root.getAttribute("type")
|
||||
val content = StringUtil.unescapeStringCharacters(root.textContent).trim()
|
||||
|
||||
when (outputType) {
|
||||
"ORDINARY" -> super.notifyTextAvailable("$content\n", key)
|
||||
"REPORT" -> {
|
||||
val report = dBuilder.parse(strToSource(content, Charsets.UTF_16BE))
|
||||
val entries = report.getElementsByTagName("reportEntry")
|
||||
for (i in 0..entries.length - 1) {
|
||||
val reportEntry = entries.item(i) as Element
|
||||
val rangeStart = reportEntry.getAttribute("rangeStart").toInt()
|
||||
val rangeEnd = reportEntry.getAttribute("rangeEnd").toInt()
|
||||
|
||||
rangeQueue.add(TextRange(rangeStart, rangeEnd))
|
||||
super.notifyTextAvailable("${StringUtil.unescapeXml(reportEntry.textContent)}\n", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.console
|
||||
|
||||
import com.intellij.execution.console.BasicGutterContentProvider
|
||||
import com.intellij.openapi.editor.event.DocumentAdapter
|
||||
import com.intellij.openapi.editor.event.DocumentEvent
|
||||
import com.intellij.openapi.editor.ex.EditorEx
|
||||
import com.intellij.openapi.editor.markup.EffectType
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.ui.JBColor
|
||||
import java.util.*
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public class KotlinReplResultHighlighter(private val editor: EditorEx) : DocumentAdapter() {
|
||||
private val EVAL_MARKERS_LENGTH = BasicGutterContentProvider.EVAL_IN_MARKER.length()
|
||||
private val ERROR_PREFIX = "Error: "
|
||||
private val WARNING_PREFIX = "Warning: "
|
||||
|
||||
var rangeQueue: Queue<TextRange> by Delegates.notNull()
|
||||
private var curLine = 0
|
||||
|
||||
private enum class LineType(val color: JBColor) {
|
||||
ERROR(JBColor.RED), WARNING(JBColor.YELLOW), USUAL(JBColor.BLACK)
|
||||
}
|
||||
|
||||
private fun lineTypeByPrefix(s: String) =
|
||||
if (s.startsWith(ERROR_PREFIX))
|
||||
LineType.ERROR
|
||||
else if (s.startsWith(WARNING_PREFIX))
|
||||
LineType.WARNING
|
||||
else
|
||||
LineType.USUAL
|
||||
|
||||
override fun documentChanged(e: DocumentEvent) {
|
||||
val document = editor.document
|
||||
val docText = document.text
|
||||
if (docText.isEmpty()) {
|
||||
curLine = 0
|
||||
return
|
||||
}
|
||||
|
||||
val lastErrorPos = docText.lastIndexOf(ERROR_PREFIX)
|
||||
val lastWarningPos = docText.lastIndexOf(WARNING_PREFIX)
|
||||
if (lastErrorPos <= curLine && lastWarningPos <= curLine) return
|
||||
|
||||
val text = document.text
|
||||
val totalLines = document.lineCount
|
||||
var codeLineOffset = -1
|
||||
while (curLine < totalLines) {
|
||||
val lineStart = document.getLineStartOffset(curLine)
|
||||
val lineEnd = document.getLineEndOffset(curLine)
|
||||
val lineText = text.substring(lineStart, lineEnd)
|
||||
val lineType = lineTypeByPrefix(lineText)
|
||||
|
||||
if (lineType != LineType.USUAL) {
|
||||
if (codeLineOffset == -1) codeLineOffset = document.getLineStartOffset(curLine - 1) + EVAL_MARKERS_LENGTH
|
||||
highlightLine(codeLineOffset, lineStart, lineEnd, lineType)
|
||||
}
|
||||
curLine++
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun highlightLine(codeLineOffset: Int, msgLineStart: Int, msgLineEne: Int, lineType: LineType) {
|
||||
val historyMarkup = editor.markupModel
|
||||
|
||||
// highlight error or warning message
|
||||
val msgTextAttributes = TextAttributes()
|
||||
msgTextAttributes.foregroundColor = lineType.color
|
||||
msgTextAttributes.backgroundColor = JBColor.LIGHT_GRAY
|
||||
historyMarkup.addRangeHighlighter(msgLineStart, msgLineEne, HighlighterLayer.LAST, msgTextAttributes, HighlighterTargetArea.LINES_IN_RANGE)
|
||||
|
||||
// highlight range in [codeLine]
|
||||
val range = rangeQueue.poll()
|
||||
val highlightedPlaceStart = codeLineOffset + range.startOffset
|
||||
val highlightedPlaceSEnd = codeLineOffset + range.endOffset + if (range.endOffset == range.startOffset) 1 else 0
|
||||
|
||||
val errorWaveAttrs = TextAttributes()
|
||||
errorWaveAttrs.effectType = EffectType.WAVE_UNDERSCORE
|
||||
errorWaveAttrs.effectColor = lineType.color
|
||||
historyMarkup.addRangeHighlighter(highlightedPlaceStart, highlightedPlaceSEnd, HighlighterLayer.LAST, errorWaveAttrs, HighlighterTargetArea.EXACT_RANGE)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* 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.console
|
||||
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
|
||||
public class KtConsoleKeyListener(private val ktConsole: KotlinConsoleRunner) : KeyAdapter() {
|
||||
private var historyPos = 0
|
||||
private var prevCaretOffset = -1
|
||||
private var unfinishedCommand = ""
|
||||
|
||||
private enum class HistoryMove {
|
||||
UP, DOWN
|
||||
}
|
||||
|
||||
public fun resetHistoryPosition() {
|
||||
historyPos = ktConsole.history.size()
|
||||
prevCaretOffset = -1
|
||||
unfinishedCommand = ""
|
||||
}
|
||||
|
||||
override fun keyReleased(e: KeyEvent): Unit = when (e.keyCode) {
|
||||
KeyEvent.VK_UP -> moveHistoryCursor(HistoryMove.UP)
|
||||
KeyEvent.VK_DOWN -> moveHistoryCursor(HistoryMove.DOWN)
|
||||
}
|
||||
|
||||
private fun moveHistoryCursor(move: HistoryMove) {
|
||||
val history = ktConsole.history
|
||||
if (history.isEmpty()) return
|
||||
|
||||
val caret = ktConsole.consoleView.consoleEditor.caretModel
|
||||
val document = ktConsole.consoleView.editorDocument
|
||||
|
||||
val curOffset = caret.offset
|
||||
val curLine = document.getLineNumber(curOffset)
|
||||
val totalLines = document.lineCount
|
||||
val isMultiline = totalLines > 1
|
||||
|
||||
when (move) {
|
||||
HistoryMove.UP -> {
|
||||
if (curLine != 0 || (isMultiline && prevCaretOffset != 0)) {
|
||||
prevCaretOffset = curOffset
|
||||
return
|
||||
}
|
||||
|
||||
if (historyPos == history.size()) {
|
||||
unfinishedCommand = document.text
|
||||
}
|
||||
|
||||
historyPos = Math.max(historyPos - 1, 0)
|
||||
WriteCommandAction.runWriteCommandAction(ktConsole.project) {
|
||||
document.setText(history[historyPos])
|
||||
caret.moveToOffset(0)
|
||||
prevCaretOffset = 0
|
||||
}
|
||||
}
|
||||
HistoryMove.DOWN -> {
|
||||
if (curLine != totalLines - 1 || (isMultiline && prevCaretOffset != document.textLength)) {
|
||||
prevCaretOffset = curOffset
|
||||
return
|
||||
}
|
||||
|
||||
historyPos = Math.min(historyPos + 1, history.size())
|
||||
WriteCommandAction.runWriteCommandAction(ktConsole.project) {
|
||||
document.setText(if (historyPos == history.size()) unfinishedCommand else history[historyPos])
|
||||
caret.moveToOffset(document.textLength)
|
||||
prevCaretOffset = document.textLength
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
/*
|
||||
* 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.console.actions
|
||||
|
||||
import com.intellij.execution.console.LanguageConsoleImpl
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.util.TextRange
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
|
||||
public class KtExecuteCommandAction(private val consoleFile: VirtualFile) : AnAction() {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return errorNotification(null, "<p>Cannot find project</p>")
|
||||
val ktConsole = KotlinConsoleKeeper.getInstance(project).getConsoleByVirtualFile(consoleFile) ?: return errorNotification(project, "<p>Action performed in not valid console</p>")
|
||||
|
||||
WriteCommandAction.runWriteCommandAction(project) {
|
||||
val consoleView = ktConsole.consoleView
|
||||
val document = consoleView.editorDocument
|
||||
val command = document.text
|
||||
|
||||
document.setText(command)
|
||||
LanguageConsoleImpl.printWithHighlighting(consoleView, consoleView.consoleEditor, TextRange(0, command.length()))
|
||||
document.setText("")
|
||||
|
||||
ktConsole.submitCommand(command)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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.console.actions
|
||||
|
||||
import com.intellij.notification.Notification
|
||||
import com.intellij.notification.NotificationType
|
||||
import com.intellij.notification.Notifications
|
||||
import com.intellij.openapi.actionSystem.AnAction
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.CommonDataKeys
|
||||
import com.intellij.openapi.diagnostic.Logger
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.module.ModuleUtilCore
|
||||
import com.intellij.openapi.project.Project
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
|
||||
fun errorNotification(project: Project?, message: String) {
|
||||
val tag = "KOTLIN REPL ERROR"
|
||||
val title = "Kotlin REPL Configuration Error"
|
||||
Notifications.Bus.notify(Notification(tag, title, message, NotificationType.ERROR), project)
|
||||
}
|
||||
|
||||
fun logError(cl: Class<*>, message: String) {
|
||||
val logger = Logger.getInstance(cl)
|
||||
logger.error(message)
|
||||
}
|
||||
|
||||
public class RunKotlinConsoleAction : AnAction() {
|
||||
override fun actionPerformed(e: AnActionEvent) {
|
||||
val project = e.project ?: return errorNotification(null, "<p>Project not found</p>")
|
||||
val module = getModule(e) ?: return errorNotification(project, "<p>Module not found</p>")
|
||||
|
||||
KotlinConsoleKeeper.getInstance(project).run(module)
|
||||
}
|
||||
|
||||
private fun getModule(e: AnActionEvent): Module? {
|
||||
val project = e.project ?: return null
|
||||
val file = CommonDataKeys.VIRTUAL_FILE.getData(e.dataContext)
|
||||
|
||||
if (file != null) {
|
||||
val moduleForFile = ModuleUtilCore.findModuleForFile(file, project)
|
||||
if (moduleForFile != null) return moduleForFile
|
||||
}
|
||||
|
||||
return ModuleManager.getInstance(project).modules.firstOrNull()
|
||||
}
|
||||
}
|
||||
@@ -51,5 +51,6 @@
|
||||
<orderEntry type="module" module-name="js.serializer" />
|
||||
<orderEntry type="module" module-name="idea-js" scope="TEST" />
|
||||
<orderEntry type="module" module-name="kotlinr" />
|
||||
<orderEntry type="module" module-name="idea-repl" scope="TEST" />
|
||||
</component>
|
||||
</module>
|
||||
+142
-127
@@ -38,20 +38,20 @@
|
||||
</component>
|
||||
</project-components>
|
||||
|
||||
<application-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.PluginStartupComponent</implementation-class>
|
||||
</component>
|
||||
<application-components>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.PluginStartupComponent</implementation-class>
|
||||
</component>
|
||||
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.versions.KotlinUpdatePluginComponent</implementation-class>
|
||||
</component>
|
||||
<component>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.versions.KotlinUpdatePluginComponent</implementation-class>
|
||||
</component>
|
||||
|
||||
<component>
|
||||
<interface-class>org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem</interface-class>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem</implementation-class>
|
||||
</component>
|
||||
</application-components>
|
||||
<component>
|
||||
<interface-class>org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem</interface-class>
|
||||
<implementation-class>org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem</implementation-class>
|
||||
</component>
|
||||
</application-components>
|
||||
|
||||
<module-components>
|
||||
<component>
|
||||
@@ -97,7 +97,7 @@
|
||||
</action>
|
||||
|
||||
<action id="ShowKotlinBytecode" class="org.jetbrains.kotlin.idea.actions.ShowKotlinBytecodeAction"
|
||||
text="Show Kotlin Bytecode">
|
||||
text="Show Kotlin Bytecode">
|
||||
<add-to-group group-id="KotlinToolsGroup"/>
|
||||
</action>
|
||||
|
||||
@@ -108,14 +108,14 @@
|
||||
|
||||
<action id="IntroduceProperty" class="org.jetbrains.kotlin.idea.refactoring.introduce.introduceVariable.IntroducePropertyAction"
|
||||
text="P_roperty..." use-shortcut-of="IntroduceField">
|
||||
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceField"/>
|
||||
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceField"/>
|
||||
</action>
|
||||
|
||||
<action id="IntroduceLambdaParameter"
|
||||
class="org.jetbrains.kotlin.idea.refactoring.introduce.introduceParameter.IntroduceLambdaParameterAction"
|
||||
text="La_mbda Parameter...">
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="control shift P"/>
|
||||
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceParameter"/>
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="control shift P"/>
|
||||
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="IntroduceParameter"/>
|
||||
</action>
|
||||
|
||||
<action id="CopyAsDiagnosticTest" class="org.jetbrains.kotlin.idea.actions.internal.CopyAsDiagnosticTestAction"
|
||||
@@ -125,12 +125,12 @@
|
||||
</action>
|
||||
|
||||
<action id="CheckPartialBodyResolve" class="org.jetbrains.kotlin.idea.actions.internal.CheckPartialBodyResolveAction"
|
||||
text="Check Partial Body Resolve">
|
||||
text="Check Partial Body Resolve">
|
||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||
</action>
|
||||
|
||||
<action id="FindImplicitNothingAction" class="org.jetbrains.kotlin.idea.actions.internal.FindImplicitNothingAction"
|
||||
text="Find Implicit Nothing Calls">
|
||||
text="Find Implicit Nothing Calls">
|
||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||
</action>
|
||||
|
||||
@@ -147,6 +147,18 @@
|
||||
<keyboard-shortcut keymap="$default" first-keystroke="control alt shift M"/>
|
||||
<add-to-group group-id="IntroduceActionsGroup" anchor="after" relative-to-action="ExtractFunction"/>
|
||||
</action>
|
||||
|
||||
<!-- Kotlin Console REPL-->
|
||||
<action id="KotlinConsoleREPL" class="org.jetbrains.kotlin.console.actions.RunKotlinConsoleAction"
|
||||
text="Kotlin console...">
|
||||
<add-to-group group-id="KotlinToolsGroup" anchor="last"/>
|
||||
</action>
|
||||
|
||||
<action id="KotlinShellExecute" class="com.intellij.openapi.actionSystem.EmptyAction"
|
||||
text="Execute Kotlin Code"
|
||||
description="Execute Kotlin code in console">
|
||||
<keyboard-shortcut first-keystroke="control ENTER" keymap="$default"/>
|
||||
</action>
|
||||
</actions>
|
||||
|
||||
<extensions defaultExtensionNs="com.intellij">
|
||||
@@ -166,7 +178,7 @@
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.FileAttributeServiceImpl"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.util.ImportInsertHelper"
|
||||
serviceImplementation="org.jetbrains.kotlin.util.ImportInsertHelperImpl"/>
|
||||
serviceImplementation="org.jetbrains.kotlin.util.ImportInsertHelperImpl"/>
|
||||
|
||||
<applicationService serviceInterface="org.jetbrains.kotlin.psi.KotlinDeclarationNavigationPolicy"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.decompiler.navigation.KotlinDeclarationNavigationPolicyImpl"/>
|
||||
@@ -184,7 +196,7 @@
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IDELightClassGenerationSupport"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.resolve.CodeAnalyzerInitializer"
|
||||
serviceImplementation="org.jetbrains.kotlin.resolve.DummyCodeAnalyzerInitializer"/>
|
||||
serviceImplementation="org.jetbrains.kotlin.resolve.DummyCodeAnalyzerInitializer"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.parsing.JetScriptDefinitionProvider"
|
||||
serviceImplementation="org.jetbrains.kotlin.parsing.JetScriptDefinitionProvider"/>
|
||||
@@ -237,6 +249,9 @@
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.idea.caches.resolve.ClsJavaStubByVirtualFileCache"
|
||||
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.ClsJavaStubByVirtualFileCache"/>
|
||||
|
||||
<projectService serviceInterface="org.jetbrains.kotlin.console.KotlinConsoleKeeper"
|
||||
serviceImplementation="org.jetbrains.kotlin.console.KotlinConsoleKeeper"/>
|
||||
|
||||
<errorHandler implementation="org.jetbrains.kotlin.idea.reporter.KotlinReportSubmitter"/>
|
||||
|
||||
@@ -920,13 +935,13 @@
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertFunctionToPropertyIntention</className>
|
||||
<category>Kotlin</category>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertFunctionToPropertyIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertPropertyToFunctionIntention</className>
|
||||
<category>Kotlin</category>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.ConvertPropertyToFunctionIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
@@ -989,20 +1004,20 @@
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.RemoveArgumentNameIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.RemoveArgumentNameIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.IterateExpressionIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.IterateExpressionIntention</className>
|
||||
<category>Kotlin</category>
|
||||
</intentionAction>
|
||||
|
||||
<intentionAction>
|
||||
<className>org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxIntention</className>
|
||||
@@ -1014,91 +1029,91 @@
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="INFO"
|
||||
/>
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ConvertToStringTemplateInspection"
|
||||
displayName="Convert string concatenation to string template"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="INFO"
|
||||
/>
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ConvertToStringTemplateInspection"
|
||||
displayName="Convert string concatenation to string template"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="INFO"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.conventionNameCalls.ExplicitGetInspection"
|
||||
displayName="Explicit 'get'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="false"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToElvisInspection"
|
||||
displayName="If-Then foldable to '?:'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
displayName="If-Then foldable to '?:'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IfThenToSafeAccessInspection"
|
||||
displayName="If-Then foldable to '?.'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
displayName="If-Then foldable to '?.'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.IfNullToElvisInspection"
|
||||
displayName="If-Null return/break/... foldable to '?:'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.IfNullToElvisInspection"
|
||||
displayName="If-Null return/break/... foldable to '?:'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsInspection"
|
||||
displayName="Type arguments are unnecessary"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveExplicitTypeArgumentsInspection"
|
||||
displayName="Type arguments are unnecessary"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveExplicitSuperQualifierInspection"
|
||||
displayName="Supertype qualification is unnecessary"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveExplicitSuperQualifierInspection"
|
||||
displayName="Supertype qualification is unnecessary"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.SimplifyNegatedBinaryExpressionInspection"
|
||||
displayName="Simplify negated binary expression"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
displayName="Simplify negated binary expression"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.ReplaceWithOperatorAssignmentInspection"
|
||||
displayName="Replace with operator-assignment"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
displayName="Replace with operator-assignment"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.branchedTransformations.intentions.IntroduceWhenSubjectInspection"
|
||||
displayName="Introduce argument to 'when'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
displayName="Introduce argument to 'when'"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateInspection"
|
||||
displayName="Remove redundant curly braces in string template"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveCurlyBracesFromTemplateInspection"
|
||||
displayName="Remove redundant curly braces in string template"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.SimplifyBooleanWithConstantsInspection"
|
||||
displayName="Simplify boolean expression with constants"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.SimplifyBooleanWithConstantsInspection"
|
||||
displayName="Simplify boolean expression with constants"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WEAK WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxInspection"
|
||||
displayName="Use property access syntax"
|
||||
@@ -1109,18 +1124,18 @@
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.UnusedSymbolInspection"
|
||||
displayName="Unused symbol"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
displayName="Unused symbol"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.UnusedReceiverParameterInspection"
|
||||
displayName="Unused receiver parameter"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
displayName="Unused receiver parameter"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.UnusedImportInspection"
|
||||
displayName="Unused import directive"
|
||||
@@ -1137,26 +1152,26 @@
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.kdoc.KDocUnresolvedReferenceInspection"
|
||||
displayName="Unresolved reference in KDoc"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
displayName="Unresolved reference in KDoc"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.refactoring.move.changePackage.PackageDirectoryMismatchInspection"
|
||||
displayName="Package name does not match containing directory"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
displayName="Package name does not match containing directory"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
level="WARNING"
|
||||
/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.inspections.KotlinCleanupInspection"
|
||||
shortName="KotlinDeprecation"
|
||||
displayName="Usage of redundant or deprecated syntax or deprecated symbols"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
cleanupTool="true"
|
||||
level="WARNING"/>
|
||||
shortName="KotlinDeprecation"
|
||||
displayName="Usage of redundant or deprecated syntax or deprecated symbols"
|
||||
groupName="Kotlin"
|
||||
enabledByDefault="true"
|
||||
cleanupTool="true"
|
||||
level="WARNING"/>
|
||||
|
||||
<localInspection implementationClass="org.jetbrains.kotlin.idea.intentions.RemoveForLoopIndicesInspection"
|
||||
displayName="Index is unused"
|
||||
@@ -1188,8 +1203,8 @@
|
||||
</extensionPoints>
|
||||
|
||||
<extensions defaultExtensionNs="org.jetbrains.kotlin">
|
||||
<xi:include href="extensions/kotlin2jvm.xml" xpointer="xpointer(/idea-plugin/extensions/*)"/>
|
||||
<xi:include href="extensions/kotlin2js.xml" xpointer="xpointer(/idea-plugin/extensions/*)"/>
|
||||
<xi:include href="extensions/kotlin2jvm.xml" xpointer="xpointer(/idea-plugin/extensions/*)"/>
|
||||
<xi:include href="extensions/kotlin2js.xml" xpointer="xpointer(/idea-plugin/extensions/*)"/>
|
||||
<quickFixContributor implementation="org.jetbrains.kotlin.idea.quickfix.QuickFixRegistrar"/>
|
||||
</extensions>
|
||||
|
||||
|
||||
@@ -0,0 +1,91 @@
|
||||
/*
|
||||
* 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.idea.console
|
||||
|
||||
import com.intellij.execution.impl.ConsoleViewImpl
|
||||
import com.intellij.openapi.Disposable
|
||||
import com.intellij.openapi.util.Disposer
|
||||
import com.intellij.testFramework.PlatformTestCase
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleRunner
|
||||
import org.junit.Test
|
||||
import kotlin.properties.Delegates
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
|
||||
public class KotlinReplTest : PlatformTestCase() {
|
||||
private var consoleRunner: KotlinConsoleRunner by Delegates.notNull()
|
||||
|
||||
override fun setUp() {
|
||||
super.setUp()
|
||||
consoleRunner = KotlinConsoleKeeper.getInstance(project).run(module)!!
|
||||
}
|
||||
|
||||
override fun tearDown() {
|
||||
val consoleView = consoleRunner.consoleView
|
||||
|
||||
consoleRunner.processHandler.destroyProcess()
|
||||
|
||||
// dispose RunContentDescriptor by reflection
|
||||
val getNodeMethod = Disposer.getTree().javaClass.getDeclaredMethod("getNode", Object().javaClass)
|
||||
getNodeMethod.isAccessible = true
|
||||
val consoleNode = getNodeMethod(Disposer.getTree(), consoleView)
|
||||
val getParentMethod = consoleNode.javaClass.getDeclaredMethod("getParent")
|
||||
getParentMethod.isAccessible = true
|
||||
val consoleParent = getParentMethod(consoleNode)
|
||||
val getObjectMethod = consoleParent.javaClass.getDeclaredMethod("getObject")
|
||||
getObjectMethod.isAccessible = true
|
||||
val descriptor = getObjectMethod(consoleParent) as Disposable
|
||||
|
||||
Disposer.dispose(descriptor)
|
||||
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
fun checkHistoryUpdate(maxIter: Int = 20, sleepTime: Long = 500, p: (String) -> Boolean): String {
|
||||
val consoleView = consoleRunner.consoleView as ConsoleViewImpl
|
||||
var docHistory: String = ""
|
||||
|
||||
for (i in 1..maxIter) {
|
||||
docHistory = consoleRunner.consoleView.historyViewer.document.text.trim()
|
||||
|
||||
if (p(docHistory)) break
|
||||
|
||||
Thread.sleep(sleepTime)
|
||||
consoleView.flushDeferredText()
|
||||
}
|
||||
|
||||
return docHistory
|
||||
}
|
||||
|
||||
@Test fun testOnRunPossibility() {
|
||||
val allOk = { x: String -> x.contains(":help for help") }
|
||||
val hasErrors = { x: String -> x.contains("Process finished with exit code 1") || x.contains("Exception in") || x.contains("Error") }
|
||||
val historyText = checkHistoryUpdate { hasErrors(it) || allOk(it) }
|
||||
|
||||
assertFalse(hasErrors(historyText), "Cannot run kotlin repl")
|
||||
assertTrue(allOk(historyText), "Successful run should contain text: ':help for help'")
|
||||
assertFalse(consoleRunner.processHandler.isProcessTerminated, "Process accidentally terminated")
|
||||
}
|
||||
|
||||
@Test fun testSimpleCommand() {
|
||||
consoleRunner.submitCommand("1 + 1")
|
||||
val docHistory = checkHistoryUpdate { x: String -> x.endsWith('2') }
|
||||
|
||||
assertTrue(docHistory.endsWith('2'), "1 + 1 should be equal 2, but document history is: '$docHistory'")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user