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)
+ }
+}
\ No newline at end of file
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/KtConsoleKeyListener.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/KtConsoleKeyListener.kt
new file mode 100644
index 00000000000..718fe14566e
--- /dev/null
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/KtConsoleKeyListener.kt
@@ -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
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/KtExecuteCommandAction.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/KtExecuteCommandAction.kt
new file mode 100644
index 00000000000..1e272fc8b35
--- /dev/null
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/KtExecuteCommandAction.kt
@@ -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, "Cannot find project
")
+ val ktConsole = KotlinConsoleKeeper.getInstance(project).getConsoleByVirtualFile(consoleFile) ?: return errorNotification(project, "Action performed in not valid console
")
+
+ 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)
+ }
+ }
+}
\ No newline at end of file
diff --git a/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/RunKotlinConsoleAction.kt b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/RunKotlinConsoleAction.kt
new file mode 100644
index 00000000000..4496514d4e3
--- /dev/null
+++ b/idea/idea-repl/src/org/jetbrains/kotlin/console/actions/RunKotlinConsoleAction.kt
@@ -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, "Project not found
")
+ val module = getModule(e) ?: return errorNotification(project, "Module not found
")
+
+ 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()
+ }
+}
\ No newline at end of file
diff --git a/idea/idea.iml b/idea/idea.iml
index 725e973dd63..b77ac1495b2 100644
--- a/idea/idea.iml
+++ b/idea/idea.iml
@@ -51,5 +51,6 @@
+
\ No newline at end of file
diff --git a/idea/src/META-INF/plugin.xml b/idea/src/META-INF/plugin.xml
index 0ea4da381f2..78f3ec38c3a 100644
--- a/idea/src/META-INF/plugin.xml
+++ b/idea/src/META-INF/plugin.xml
@@ -38,20 +38,20 @@
-
-
- org.jetbrains.kotlin.idea.PluginStartupComponent
-
+
+
+ org.jetbrains.kotlin.idea.PluginStartupComponent
+
-
- org.jetbrains.kotlin.idea.versions.KotlinUpdatePluginComponent
-
+
+ org.jetbrains.kotlin.idea.versions.KotlinUpdatePluginComponent
+
-
- org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem
- org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem
-
-
+
+ org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem
+ org.jetbrains.kotlin.idea.js.KotlinJavaScriptMetaFileSystem
+
+
@@ -97,7 +97,7 @@
+ text="Show Kotlin Bytecode">
@@ -108,14 +108,14 @@
-
+
-
-
+
+
+ text="Check Partial Body Resolve">
+ text="Find Implicit Nothing Calls">
@@ -147,6 +147,18 @@
+
+
+
+
+
+
+
+
+
@@ -166,7 +178,7 @@
serviceImplementation="org.jetbrains.kotlin.idea.caches.FileAttributeServiceImpl"/>
+ serviceImplementation="org.jetbrains.kotlin.util.ImportInsertHelperImpl"/>
@@ -184,7 +196,7 @@
serviceImplementation="org.jetbrains.kotlin.idea.caches.resolve.IDELightClassGenerationSupport"/>
+ serviceImplementation="org.jetbrains.kotlin.resolve.DummyCodeAnalyzerInitializer"/>
@@ -237,6 +249,9 @@
+
+
@@ -920,13 +935,13 @@
- org.jetbrains.kotlin.idea.intentions.ConvertFunctionToPropertyIntention
- Kotlin
+ org.jetbrains.kotlin.idea.intentions.ConvertFunctionToPropertyIntention
+ Kotlin
- org.jetbrains.kotlin.idea.intentions.ConvertPropertyToFunctionIntention
- Kotlin
+ org.jetbrains.kotlin.idea.intentions.ConvertPropertyToFunctionIntention
+ Kotlin
@@ -989,20 +1004,20 @@
Kotlin
-
- org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention
- Kotlin
-
+
+ org.jetbrains.kotlin.idea.intentions.AddNameToArgumentIntention
+ Kotlin
+
-
- org.jetbrains.kotlin.idea.intentions.RemoveArgumentNameIntention
- Kotlin
-
+
+ org.jetbrains.kotlin.idea.intentions.RemoveArgumentNameIntention
+ Kotlin
+
-
- org.jetbrains.kotlin.idea.intentions.IterateExpressionIntention
- Kotlin
-
+
+ org.jetbrains.kotlin.idea.intentions.IterateExpressionIntention
+ Kotlin
+
org.jetbrains.kotlin.idea.intentions.UsePropertyAccessSyntaxIntention
@@ -1014,91 +1029,91 @@
groupName="Kotlin"
enabledByDefault="true"
level="INFO"
- />
+ />
-
+
+ />
+ displayName="If-Then foldable to '?:'"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WEAK WARNING"
+ />
+ displayName="If-Then foldable to '?.'"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WEAK WARNING"
+ />
-
+
-
+
-
+
+ displayName="Simplify negated binary expression"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WEAK WARNING"
+ />
+ displayName="Replace with operator-assignment"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WEAK WARNING"
+ />
+ displayName="Introduce argument to 'when'"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WEAK WARNING"
+ />
-
+
-
+
+ displayName="Unused symbol"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WARNING"
+ />
+ displayName="Unused receiver parameter"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WARNING"
+ />
+ displayName="Unresolved reference in KDoc"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WARNING"
+ />
+ displayName="Package name does not match containing directory"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ level="WARNING"
+ />
+ shortName="KotlinDeprecation"
+ displayName="Usage of redundant or deprecated syntax or deprecated symbols"
+ groupName="Kotlin"
+ enabledByDefault="true"
+ cleanupTool="true"
+ level="WARNING"/>
-
-
+
+
diff --git a/idea/tests/org/jetbrains/kotlin/idea/console/KotlinReplTest.kt b/idea/tests/org/jetbrains/kotlin/idea/console/KotlinReplTest.kt
new file mode 100644
index 00000000000..8aab992d944
--- /dev/null
+++ b/idea/tests/org/jetbrains/kotlin/idea/console/KotlinReplTest.kt
@@ -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'")
+ }
+}
\ No newline at end of file