diff --git a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt index a2758c06b3f..344b0161608 100755 --- a/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt +++ b/generators/src/org/jetbrains/kotlin/generators/tests/GenerateTests.kt @@ -129,6 +129,7 @@ import org.jetbrains.kotlin.idea.refactoring.safeDelete.AbstractMultiModuleSafeD import org.jetbrains.kotlin.idea.refactoring.safeDelete.AbstractSafeDeleteTest import org.jetbrains.kotlin.idea.repl.AbstractIdeReplCompletionTest import org.jetbrains.kotlin.idea.resolve.* +import org.jetbrains.kotlin.idea.scratch.AbstractScratchRunActionTest import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationCompletionTest import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationHighlightingTest import org.jetbrains.kotlin.idea.script.AbstractScriptConfigurationNavigationTest @@ -732,6 +733,10 @@ fun main(args: Array) { testClass { model("slicer/inflow", singleClass = true) } + + testClass { + model("scratch", extension = "kts") + } } testGroup("idea/idea-maven/test", "idea/idea-maven/testData") { diff --git a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/repl/KtScratchReplExecutor.kt b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/repl/KtScratchReplExecutor.kt index 67aba0a74cc..d66b692b0cd 100644 --- a/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/repl/KtScratchReplExecutor.kt +++ b/idea/idea-jvm/src/org/jetbrains/kotlin/idea/scratch/repl/KtScratchReplExecutor.kt @@ -30,6 +30,7 @@ import org.jetbrains.kotlin.idea.scratch.ScratchExpression import org.jetbrains.kotlin.idea.scratch.ScratchFile import org.jetbrains.kotlin.idea.scratch.output.ScratchOutput import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputType +import org.jetbrains.kotlin.idea.scratch.ui.scratchTopPanel import org.w3c.dom.Element import org.xml.sax.InputSource import java.io.ByteArrayInputStream @@ -45,7 +46,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) { override fun execute() { handlers.forEach { it.onStart(file) } - val module = file.module ?: return error(file, "Module should be selected") + val module = file.scratchTopPanel?.getModule() ?: return error(file, "Module should be selected") val cmdLine = KotlinConsoleKeeper.createCommandLine(module) osProcessHandler = ReplOSProcessHandler(cmdLine) diff --git a/idea/testData/scratch/for.kts b/idea/testData/scratch/for.kts new file mode 100644 index 00000000000..afc542c82dc --- /dev/null +++ b/idea/testData/scratch/for.kts @@ -0,0 +1,3 @@ +for (i in 0..5) { + println(i) +} diff --git a/idea/testData/scratch/for.repl.after b/idea/testData/scratch/for.repl.after new file mode 100644 index 00000000000..0a7a6928b84 --- /dev/null +++ b/idea/testData/scratch/for.repl.after @@ -0,0 +1,3 @@ +for (i in 0..5) { // OUTPUT: 0; 1; 2; 3; 4; 5 + println(i) +} diff --git a/idea/testData/scratch/simple.kts b/idea/testData/scratch/simple.kts new file mode 100644 index 00000000000..c49c0a3cb30 --- /dev/null +++ b/idea/testData/scratch/simple.kts @@ -0,0 +1,2 @@ +val a = 1 +a \ No newline at end of file diff --git a/idea/testData/scratch/simple.repl.after b/idea/testData/scratch/simple.repl.after new file mode 100644 index 00000000000..0ffc50ba557 --- /dev/null +++ b/idea/testData/scratch/simple.repl.after @@ -0,0 +1,2 @@ +val a = 1 +a // RESULT: 1 diff --git a/idea/testData/scratch/simpleFun.kts b/idea/testData/scratch/simpleFun.kts new file mode 100644 index 00000000000..70ff7f93dac --- /dev/null +++ b/idea/testData/scratch/simpleFun.kts @@ -0,0 +1,5 @@ +fun foo(): Int { + return 1 +} + +foo() \ No newline at end of file diff --git a/idea/testData/scratch/simpleFun.repl.after b/idea/testData/scratch/simpleFun.repl.after new file mode 100644 index 00000000000..56b3a2ca52a --- /dev/null +++ b/idea/testData/scratch/simpleFun.repl.after @@ -0,0 +1,5 @@ +fun foo(): Int { + return 1 +} + +foo() // RESULT: 1 diff --git a/idea/testData/scratch/stdlibFun.comp.after b/idea/testData/scratch/stdlibFun.comp.after new file mode 100644 index 00000000000..556ab826a4c --- /dev/null +++ b/idea/testData/scratch/stdlibFun.comp.after @@ -0,0 +1,3 @@ +arrayListOf(1, 5, 7).map { it * 2 } // RESULT: 2 + .filter { it < 10 } + .find { it == 2 } diff --git a/idea/testData/scratch/stdlibFun.kts b/idea/testData/scratch/stdlibFun.kts new file mode 100644 index 00000000000..c7a1f9d966d --- /dev/null +++ b/idea/testData/scratch/stdlibFun.kts @@ -0,0 +1,3 @@ +arrayListOf(1, 5, 7).map { it * 2 } + .filter { it < 10 } + .find { it == 2 } \ No newline at end of file diff --git a/idea/testData/scratch/stdlibFun.repl.after b/idea/testData/scratch/stdlibFun.repl.after new file mode 100644 index 00000000000..556ab826a4c --- /dev/null +++ b/idea/testData/scratch/stdlibFun.repl.after @@ -0,0 +1,3 @@ +arrayListOf(1, 5, 7).map { it * 2 } // RESULT: 2 + .filter { it < 10 } + .find { it == 2 } diff --git a/idea/testData/scratch/unresolved.kts b/idea/testData/scratch/unresolved.kts new file mode 100644 index 00000000000..a57d16823ff --- /dev/null +++ b/idea/testData/scratch/unresolved.kts @@ -0,0 +1 @@ +foo() \ No newline at end of file diff --git a/idea/testData/scratch/unresolved.repl.after b/idea/testData/scratch/unresolved.repl.after new file mode 100644 index 00000000000..89ce57b2b54 --- /dev/null +++ b/idea/testData/scratch/unresolved.repl.after @@ -0,0 +1 @@ +foo() // ERROR: error: unresolved reference: foo diff --git a/idea/testData/scratch/userOutput.kts b/idea/testData/scratch/userOutput.kts new file mode 100644 index 00000000000..ae973bcd537 --- /dev/null +++ b/idea/testData/scratch/userOutput.kts @@ -0,0 +1 @@ +println("hello") \ No newline at end of file diff --git a/idea/testData/scratch/userOutput.repl.after b/idea/testData/scratch/userOutput.repl.after new file mode 100644 index 00000000000..ce696ae6bfb --- /dev/null +++ b/idea/testData/scratch/userOutput.repl.after @@ -0,0 +1 @@ +println("hello") // OUTPUT: hello diff --git a/idea/tests/org/jetbrains/kotlin/idea/scratch/AbstractScratchRunActionTest.kt b/idea/tests/org/jetbrains/kotlin/idea/scratch/AbstractScratchRunActionTest.kt new file mode 100644 index 00000000000..a02d8bbde66 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/scratch/AbstractScratchRunActionTest.kt @@ -0,0 +1,101 @@ +/* + * Copyright 2010-2017 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.scratch + +import com.intellij.ide.scratch.ScratchFileService +import com.intellij.ide.scratch.ScratchRootType +import com.intellij.openapi.actionSystem.AnAction +import com.intellij.openapi.actionSystem.CommonDataKeys +import com.intellij.openapi.fileEditor.FileEditorManager +import com.intellij.openapi.fileEditor.TextEditor +import com.intellij.openapi.project.Project +import com.intellij.openapi.vfs.VirtualFile +import com.intellij.psi.PsiDocumentManager +import com.intellij.testFramework.FileEditorManagerTestCase +import com.intellij.testFramework.MapDataContext +import com.intellij.testFramework.TestActionEvent +import com.intellij.util.ui.UIUtil +import org.jetbrains.kotlin.idea.KotlinLanguage +import org.jetbrains.kotlin.idea.scratch.actions.RunScratchAction +import org.jetbrains.kotlin.idea.scratch.output.InlayScratchOutputHandler +import org.jetbrains.kotlin.test.KotlinTestUtils +import org.junit.Assert +import java.io.File + +abstract class AbstractScratchRunActionTest : FileEditorManagerTestCase() { + fun doTest(fileName: String) { + val sourceFile = File(testDataPath, fileName) + val fileText = sourceFile.readText() + + val scratchFile = ScratchRootType.getInstance().createScratchFile( + project, + sourceFile.name, + KotlinLanguage.INSTANCE, + fileText, + ScratchFileService.Option.create_new_always + ) ?: error("Couldn't create scratch file ${sourceFile.path}") + + myFixture.openFileInEditor(scratchFile) + + val event = getActionEvent(myFixture.file.virtualFile, RunScratchAction()) + launchAction(event, RunScratchAction()) + + val start = System.currentTimeMillis() + // wait until output is displayed in editor or for 1 minute + while (!event.presentation.isEnabled && (System.currentTimeMillis() - start) < 60000) { + Thread.sleep(5000) + } + + UIUtil.dispatchAllInvocationEvents() + + val editors = FileEditorManager.getInstance(project).getEditors(myFixture.file.virtualFile).filterIsInstance() + val doc = + PsiDocumentManager.getInstance(project).getDocument(myFixture.file) ?: error("Document for ${myFixture.file.name} is null") + + val actualOutput = StringBuilder(myFixture.file.text) + for (line in doc.lineCount - 1 downTo 0) { + editors.flatMap { it.editor.inlayModel.getInlineElementsInRange(doc.getLineStartOffset(line), doc.getLineEndOffset(line)) } + .map { it.renderer } + .filterIsInstance() + .forEach { + val str = it.toString() + + val offset = doc.getLineEndOffset(line) + actualOutput.insert(offset, " // $str") + } + } + + val expectedFileName = fileName.replace(".kts", ".repl.after") + val expectedFile = File("$testDataPath/$expectedFileName") + KotlinTestUtils.assertEqualsToFile(expectedFile, actualOutput.toString()) + } + + private fun launchAction(e: TestActionEvent, action: AnAction) { + action.beforeActionPerformedUpdate(e) + Assert.assertTrue(e.presentation.isEnabled && e.presentation.isVisible) + action.actionPerformed(e) + } + + private fun getActionEvent(virtualFile: VirtualFile, action: AnAction): TestActionEvent { + val context = MapDataContext() + context.put(CommonDataKeys.VIRTUAL_FILE_ARRAY, arrayOf(virtualFile)) + context.put(CommonDataKeys.PROJECT, myFixture.project) + return TestActionEvent(context, action) + } + + override fun getTestDataPath() = KotlinTestUtils.getHomeDirectory() +} \ No newline at end of file diff --git a/idea/tests/org/jetbrains/kotlin/idea/scratch/ScratchRunActionTestGenerated.java b/idea/tests/org/jetbrains/kotlin/idea/scratch/ScratchRunActionTestGenerated.java new file mode 100644 index 00000000000..d2db2dc1f35 --- /dev/null +++ b/idea/tests/org/jetbrains/kotlin/idea/scratch/ScratchRunActionTestGenerated.java @@ -0,0 +1,63 @@ +/* + * Copyright 2000-2018 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license + * that can be found in the license/LICENSE.txt file. + */ + +package org.jetbrains.kotlin.idea.scratch; + +import com.intellij.testFramework.TestDataPath; +import org.jetbrains.kotlin.test.JUnit3RunnerWithInners; +import org.jetbrains.kotlin.test.KotlinTestUtils; +import org.jetbrains.kotlin.test.TargetBackend; +import org.jetbrains.kotlin.test.TestMetadata; +import org.junit.runner.RunWith; + +import java.io.File; +import java.util.regex.Pattern; + +/** This class is generated by {@link org.jetbrains.kotlin.generators.tests.TestsPackage}. DO NOT MODIFY MANUALLY */ +@SuppressWarnings("all") +@TestMetadata("idea/testData/scratch") +@TestDataPath("$PROJECT_ROOT") +@RunWith(JUnit3RunnerWithInners.class) +public class ScratchRunActionTestGenerated extends AbstractScratchRunActionTest { + public void testAllFilesPresentInScratch() throws Exception { + KotlinTestUtils.assertAllTestsPresentByMetadata(this.getClass(), new File("idea/testData/scratch"), Pattern.compile("^(.+)\\.kts$"), TargetBackend.ANY, true); + } + + @TestMetadata("for.kts") + public void testFor() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/scratch/for.kts"); + doTest(fileName); + } + + @TestMetadata("simple.kts") + public void testSimple() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/scratch/simple.kts"); + doTest(fileName); + } + + @TestMetadata("simpleFun.kts") + public void testSimpleFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/scratch/simpleFun.kts"); + doTest(fileName); + } + + @TestMetadata("stdlibFun.kts") + public void testStdlibFun() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/scratch/stdlibFun.kts"); + doTest(fileName); + } + + @TestMetadata("unresolved.kts") + public void testUnresolved() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/scratch/unresolved.kts"); + doTest(fileName); + } + + @TestMetadata("userOutput.kts") + public void testUserOutput() throws Exception { + String fileName = KotlinTestUtils.navigationMetadata("idea/testData/scratch/userOutput.kts"); + doTest(fileName); + } +}