Add kotlin console repl
This commit is contained in:
committed by
Pavel V. Talanov
parent
80cbee83ee
commit
a11f411f7d
@@ -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()
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user