[ide-console] Highlight repl process output
Add icons on gutter Send messages to process in xml
This commit is contained in:
committed by
Pavel V. Talanov
parent
cb1756f5fa
commit
cb5ff094bf
@@ -114,9 +114,9 @@ public class ReplFromTerminal {
|
||||
|
||||
private void doRun() {
|
||||
try {
|
||||
replWriter.println("Welcome to Kotlin version " + KotlinVersion.VERSION +
|
||||
replWriter.printlnInit("Welcome to Kotlin version " + KotlinVersion.VERSION +
|
||||
" (JRE " + System.getProperty("java.runtime.version") + ")");
|
||||
replWriter.println("Type :help for help, :quit for quit");
|
||||
replWriter.printlnInit("Type :help for help, :quit for quit");
|
||||
WhatNextAfterOneLine next = WhatNextAfterOneLine.READ_LINE;
|
||||
while (true) {
|
||||
next = one(next);
|
||||
|
||||
@@ -48,7 +48,7 @@ fun PsiElement.getModuleInfo(): IdeaModuleInfo {
|
||||
)
|
||||
}
|
||||
|
||||
val explicitModuleInfo = containingJetFile?.moduleInfo
|
||||
val explicitModuleInfo = containingJetFile?.moduleInfo ?: (containingJetFile?.originalFile as? JetFile)?.moduleInfo
|
||||
if (explicitModuleInfo is IdeaModuleInfo) return explicitModuleInfo
|
||||
|
||||
if (containingJetFile is JetCodeFragment) {
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="library" name="idea-full" level="project" />
|
||||
<orderEntry type="module" module-name="frontend" />
|
||||
<orderEntry type="module" module-name="idea-analysis" />
|
||||
<orderEntry type="module" module-name="util" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* 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.BaseOSProcessHandler
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.util.text.StringUtil
|
||||
import org.jetbrains.kotlin.console.actions.logError
|
||||
import org.jetbrains.kotlin.console.highlight.KotlinHistoryHighlighter
|
||||
|
||||
public class KotlinConsoleExecutor(
|
||||
private val runner: KotlinConsoleRunner,
|
||||
private val historyManager: KotlinConsoleHistoryManager
|
||||
) {
|
||||
private val XML_PREFIX = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
|
||||
private val historyHighlighter = KotlinHistoryHighlighter(runner)
|
||||
|
||||
fun executeCommand() = WriteCommandAction.runWriteCommandAction(runner.project) {
|
||||
val consoleView = runner.consoleView
|
||||
val document = consoleView.editorDocument
|
||||
val inputText = document.text.trim()
|
||||
|
||||
if (inputText.isNotEmpty()) {
|
||||
val command = "$inputText\n"
|
||||
document.setText("")
|
||||
|
||||
historyHighlighter.addAndHighlightNewCommand(command)
|
||||
submitCommand(command)
|
||||
}
|
||||
}
|
||||
|
||||
private fun submitCommand(command: String) {
|
||||
historyManager.updateHistory(command)
|
||||
|
||||
val processHandler = runner.processHandler
|
||||
val processInputOS = processHandler.processInput ?: return logError(javaClass, "<p>Broken process stream</p>")
|
||||
val charset = (processHandler as? BaseOSProcessHandler)?.charset ?: Charsets.UTF_8
|
||||
|
||||
val xmlRes = "$XML_PREFIX<input>${StringUtil.escapeXml(StringUtil.escapeLineBreak(command.trim()))}</input>"
|
||||
val bytes = ("$xmlRes\n").toByteArray(charset)
|
||||
processInputOS.write(bytes)
|
||||
processInputOS.flush()
|
||||
}
|
||||
}
|
||||
+24
-10
@@ -17,31 +17,45 @@
|
||||
package org.jetbrains.kotlin.console
|
||||
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import org.jetbrains.kotlin.console.highlight.ReplOutputType
|
||||
import java.awt.event.KeyAdapter
|
||||
import java.awt.event.KeyEvent
|
||||
|
||||
public class KtConsoleKeyListener(private val ktConsole: KotlinConsoleRunner) : KeyAdapter() {
|
||||
public class KotlinConsoleHistoryManager(private val ktConsole: KotlinConsoleRunner) : KeyAdapter() {
|
||||
private val history = arrayListOf<String>()
|
||||
|
||||
private var historyPos = 0
|
||||
private var prevCaretOffset = -1
|
||||
private var unfinishedCommand = ""
|
||||
|
||||
var lastCommandType = ReplOutputType.USER_OUTPUT
|
||||
|
||||
val lastCommandLength: Int
|
||||
get() = history.last().length()
|
||||
|
||||
public fun updateHistory(command: String) {
|
||||
if (lastCommandType == ReplOutputType.INCOMPLETE)
|
||||
history[history.lastIndex] = "${history.last()}$command"
|
||||
else
|
||||
history.add(command)
|
||||
|
||||
// reset history positions
|
||||
historyPos = history.size()
|
||||
prevCaretOffset = -1
|
||||
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)
|
||||
KeyEvent.VK_UP -> moveHistoryCursor(HistoryMove.UP)
|
||||
KeyEvent.VK_DOWN -> moveHistoryCursor(HistoryMove.DOWN)
|
||||
KeyEvent.VK_LEFT, KeyEvent.VK_RIGHT -> prevCaretOffset = ktConsole.consoleView.consoleEditor.caretModel.offset
|
||||
}
|
||||
|
||||
private fun moveHistoryCursor(move: HistoryMove) {
|
||||
val history = ktConsole.history
|
||||
if (history.isEmpty()) return
|
||||
|
||||
val caret = ktConsole.consoleView.consoleEditor.caretModel
|
||||
@@ -42,13 +42,9 @@ public class KotlinConsoleKeeper(val project: Project) {
|
||||
|
||||
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 cmdLine = createCommandLine(module) ?: return errorNotification(project, "<p>Module SDK not found</p>") let { null }
|
||||
|
||||
val consoleRunner = KotlinConsoleRunner(path, cmdLine, project, REPL_TITLE)
|
||||
val consoleRunner = KotlinConsoleRunner(path, cmdLine, module, project, REPL_TITLE)
|
||||
consoleRunner.initAndRun()
|
||||
consoleRunner.setupGutters()
|
||||
|
||||
|
||||
@@ -22,58 +22,53 @@ 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.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.psi.PsiManager
|
||||
import org.jetbrains.kotlin.console.actions.KtExecuteCommandAction
|
||||
import org.jetbrains.kotlin.console.actions.logError
|
||||
import org.jetbrains.kotlin.console.gutter.KotlinConsoleGutterContentProvider
|
||||
import org.jetbrains.kotlin.console.gutter.KotlinConsoleIndicatorRenderer
|
||||
import org.jetbrains.kotlin.console.gutter.ReplIcons
|
||||
import org.jetbrains.kotlin.console.highlight.KotlinReplOutputHighlighter
|
||||
import org.jetbrains.kotlin.console.highlight.ReplColors
|
||||
import org.jetbrains.kotlin.idea.JetLanguage
|
||||
import org.jetbrains.kotlin.idea.caches.resolve.productionSourceInfo
|
||||
import org.jetbrains.kotlin.psi.JetFile
|
||||
import org.jetbrains.kotlin.psi.moduleInfo
|
||||
import java.awt.Color
|
||||
import java.awt.Font
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
import javax.swing.Icon
|
||||
import kotlin.properties.Delegates
|
||||
|
||||
public class KotlinConsoleRunner(
|
||||
val title: String,
|
||||
private val title: String,
|
||||
private val cmdLine: GeneralCommandLine,
|
||||
private val module: Module,
|
||||
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()
|
||||
private val editorToIndicator = ConcurrentHashMap<EditorEx, KotlinConsoleIndicatorRenderer>()
|
||||
private val historyManager = KotlinConsoleHistoryManager(this)
|
||||
val executor = KotlinConsoleExecutor(this, historyManager)
|
||||
|
||||
override fun createProcess() = cmdLine.createProcess()
|
||||
|
||||
override fun createConsoleView(): LanguageConsoleView? {
|
||||
val consoleView = LanguageConsoleBuilder().build(project, JetLanguage.INSTANCE)
|
||||
val consoleView = LanguageConsoleBuilder()
|
||||
.gutterContentProvider(KotlinConsoleGutterContentProvider())
|
||||
.build(project, JetLanguage.INSTANCE)
|
||||
|
||||
consoleView.prompt = null
|
||||
configureModuleForConsoleFile(consoleView)
|
||||
|
||||
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
|
||||
setupPlaceholder(consoleEditor)
|
||||
consoleEditor.contentComponent.addKeyListener(historyManager)
|
||||
|
||||
val executeAction = KtExecuteCommandAction(consoleView.virtualFile)
|
||||
executeAction.registerCustomShortcutSet(CommonShortcuts.CTRL_ENTER, consoleView.consoleEditor.component)
|
||||
@@ -82,25 +77,20 @@ public class KotlinConsoleRunner(
|
||||
}
|
||||
|
||||
override fun createProcessHandler(process: Process): OSProcessHandler {
|
||||
val processHandler = KotlinReplOutputHandler(process, cmdLine.commandLineString)
|
||||
historyHighlighter.rangeQueue = processHandler.rangeQueue
|
||||
val processHandler = KotlinReplOutputHandler(KotlinReplOutputHighlighter(this, historyManager), process, cmdLine.commandLineString)
|
||||
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)
|
||||
}
|
||||
override fun processTerminated(_: ProcessEvent) { keeper.removeConsole(consoleFile) }
|
||||
})
|
||||
|
||||
return processHandler
|
||||
}
|
||||
|
||||
override fun createExecuteActionHandler() = object : ProcessBackedConsoleExecuteActionHandler(processHandler, false) {
|
||||
override fun sendText(line: String) {
|
||||
submitCommand(line)
|
||||
}
|
||||
override fun runExecuteAction(_: LanguageConsoleView) = executor.executeCommand()
|
||||
}
|
||||
|
||||
override fun fillToolBarActions(toolbarActions: DefaultActionGroup,
|
||||
@@ -118,6 +108,22 @@ public class KotlinConsoleRunner(
|
||||
override fun createConsoleExecAction(consoleExecuteActionHandler: ProcessBackedConsoleExecuteActionHandler)
|
||||
= ConsoleExecuteAction(consoleView, consoleExecuteActionHandler, "KotlinShellExecute", consoleExecuteActionHandler)
|
||||
|
||||
private fun setupPlaceholder(editor: EditorEx) {
|
||||
editor.setPlaceholder("<Ctrl+Enter> to execute")
|
||||
editor.setShowPlaceholderWhenFocused(true)
|
||||
|
||||
val placeholderAttrs = TextAttributes()
|
||||
placeholderAttrs.foregroundColor = ReplColors.PLACEHOLDER_COLOR
|
||||
placeholderAttrs.fontType = Font.ITALIC
|
||||
editor.setPlaceholderAttributes(placeholderAttrs)
|
||||
}
|
||||
|
||||
private fun configureModuleForConsoleFile(consoleView: LanguageConsoleView) {
|
||||
val consoleFile = consoleView.virtualFile
|
||||
val jetFile = PsiManager.getInstance(project).findFile(consoleFile) as JetFile
|
||||
jetFile.moduleInfo = module.productionSourceInfo()
|
||||
}
|
||||
|
||||
fun setupGutters() {
|
||||
fun configureEditorGutter(editor: EditorEx, color: Color, icon: Icon) {
|
||||
editor.settings.isLineMarkerAreaShown = true // hack to show gutter
|
||||
@@ -127,37 +133,27 @@ public class KotlinConsoleRunner(
|
||||
editorColorScheme.setColor(EditorColors.GUTTER_BACKGROUND, color)
|
||||
editor.colorsScheme = editorColorScheme
|
||||
|
||||
addGutterIcon(editor, icon)
|
||||
addGutterIndicator(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)
|
||||
configureEditorGutter(consoleView.historyViewer, ReplColors.HISTORY_GUTTER_COLOR, ReplIcons.HISTORY_INDICATOR)
|
||||
configureEditorGutter(consoleView.consoleEditor, ReplColors.EDITOR_GUTTER_COLOR, ReplIcons.EDITOR_INDICATOR)
|
||||
|
||||
consoleView.consoleEditor.settings.isCaretRowShown = true
|
||||
}
|
||||
|
||||
private fun addGutterIcon(editor: EditorEx, icon: Icon) {
|
||||
fun addGutterIndicator(editor: EditorEx, icon: Icon) {
|
||||
val indicator = KotlinConsoleIndicatorRenderer(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
|
||||
}
|
||||
val indicatorHighlighter = editorMarkup.addRangeHighlighter(
|
||||
0, editor.document.textLength, HighlighterLayer.LAST, null, HighlighterTargetArea.LINES_IN_RANGE
|
||||
)
|
||||
|
||||
indicatorHighlighter.gutterIconRenderer = indicator
|
||||
editorToIndicator[editor] = indicator
|
||||
}
|
||||
|
||||
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()
|
||||
fun changeEditorIndicatorIcon(editor: EditorEx, newIcon: Icon) {
|
||||
editorToIndicator[editor]?.indicatorIcon = newIcon
|
||||
}
|
||||
}
|
||||
@@ -20,21 +20,23 @@ 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.jetbrains.kotlin.console.highlight.KotlinReplOutputHighlighter
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
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
|
||||
|
||||
data class SeverityDetails(val severity: Severity, val description: String, val range: TextRange)
|
||||
|
||||
public class KotlinReplOutputHandler(
|
||||
private val outputHighlighter: KotlinReplOutputHighlighter,
|
||||
process: Process,
|
||||
commandLine: String
|
||||
) : OSProcessHandler(process, commandLine) {
|
||||
private val XML_START = "<?xml"
|
||||
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
|
||||
@@ -46,21 +48,38 @@ public class KotlinReplOutputHandler(
|
||||
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))
|
||||
"INITIAL_PROMPT" -> super.notifyTextAvailable("$content\n", key)
|
||||
"USER_OUTPUT" -> outputHighlighter.printUserOutput(content)
|
||||
"REPL_RESULT" -> outputHighlighter.printResultWithGutterIcon("$content\n")
|
||||
"REPL_INCOMPLETE" -> outputHighlighter.changeIndicatorOnIncomplete()
|
||||
"ERROR" -> {
|
||||
val compilerMessages = arrayListOf<SeverityDetails>()
|
||||
|
||||
val report = dBuilder.parse(strToSource(content, Charsets.UTF_16))
|
||||
val entries = report.getElementsByTagName("reportEntry")
|
||||
for (i in 0..entries.length - 1) {
|
||||
val reportEntry = entries.item(i) as Element
|
||||
|
||||
val severityLevel = reportEntry.getAttribute("severity").toSeverity()
|
||||
val rangeStart = reportEntry.getAttribute("rangeStart").toInt()
|
||||
val rangeEnd = reportEntry.getAttribute("rangeEnd").toInt()
|
||||
val description = reportEntry.textContent
|
||||
|
||||
rangeQueue.add(TextRange(rangeStart, rangeEnd))
|
||||
super.notifyTextAvailable("${StringUtil.unescapeXml(reportEntry.textContent)}\n", key)
|
||||
compilerMessages.add(SeverityDetails(severityLevel, description, TextRange(rangeStart, rangeEnd)))
|
||||
}
|
||||
|
||||
outputHighlighter.highlightErrors(compilerMessages)
|
||||
}
|
||||
else -> super.notifyTextAvailable("UNEXPECTED TEXT: $content\n", key)
|
||||
}
|
||||
}
|
||||
|
||||
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
|
||||
|
||||
private fun String.toSeverity() = when (this) {
|
||||
"ERROR" -> Severity.ERROR
|
||||
"WARNING" -> Severity.WARNING
|
||||
"INFO" -> Severity.INFO
|
||||
else -> throw IllegalArgumentException("Unsupported Severity: '$this'") // this case shouldn't occur
|
||||
}
|
||||
}
|
||||
@@ -1,101 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
@@ -1,44 +0,0 @@
|
||||
/*
|
||||
* 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
+10
@@ -27,6 +27,7 @@ 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 com.intellij.openapi.vfs.VirtualFile
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
|
||||
|
||||
fun errorNotification(project: Project?, message: String) {
|
||||
@@ -59,4 +60,13 @@ public class RunKotlinConsoleAction : AnAction() {
|
||||
|
||||
return ModuleManager.getInstance(project).modules.firstOrNull()
|
||||
}
|
||||
}
|
||||
|
||||
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 an invalid console</p>")
|
||||
|
||||
ktConsole.executor.executeCommand()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* 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.gutter
|
||||
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import org.jetbrains.kotlin.console.SeverityDetails
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
|
||||
public class KotlinConsoleErrorRenderer(private val messages: List<SeverityDetails>) : GutterIconRenderer() {
|
||||
private fun msgType(severity: Severity) = when (severity) {
|
||||
Severity.ERROR -> "Error:"
|
||||
Severity.WARNING -> "Warning:"
|
||||
Severity.INFO -> "Info:"
|
||||
}
|
||||
|
||||
override fun getTooltipText(): String {
|
||||
val htmlTooltips = messages map { "<b>${msgType(it.severity)}</b> ${it.description}" }
|
||||
return "<html>${htmlTooltips.join("<hr size=1 noshade>")}</html>"
|
||||
}
|
||||
|
||||
override fun getIcon() = ReplIcons.ERROR
|
||||
override fun hashCode() = System.identityHashCode(this)
|
||||
override fun equals(other: Any?) = this === other
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.gutter
|
||||
|
||||
import com.intellij.execution.console.BasicGutterContentProvider
|
||||
import com.intellij.openapi.editor.Editor
|
||||
|
||||
public class KotlinConsoleGutterContentProvider : BasicGutterContentProvider() {
|
||||
/**
|
||||
* This method overriding is needed to prevent [BasicGutterContentProvider] from adding some strange unicode
|
||||
* symbols of zero width and to ease range highlighting.
|
||||
*/
|
||||
override fun beforeEvaluate(_: Editor) = Unit
|
||||
}
|
||||
+26
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* 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.gutter
|
||||
|
||||
import com.intellij.openapi.editor.markup.GutterIconRenderer
|
||||
import javax.swing.Icon
|
||||
|
||||
public class KotlinConsoleIndicatorRenderer(var indicatorIcon: Icon) : GutterIconRenderer() {
|
||||
override fun getIcon() = indicatorIcon
|
||||
override fun hashCode() = System.identityHashCode(this)
|
||||
override fun equals(other: Any?) = this === other
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* 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.gutter
|
||||
|
||||
import com.intellij.icons.AllIcons
|
||||
import javax.swing.Icon
|
||||
|
||||
public object ReplIcons {
|
||||
public val HISTORY_INDICATOR: Icon = AllIcons.General.MessageHistory
|
||||
public val EDITOR_INDICATOR: Icon = AllIcons.Debugger.CommandLine
|
||||
public val INCOMPLETE_INDICATOR: Icon = AllIcons.Nodes.Desktop
|
||||
public val RESULT: Icon = AllIcons.Vcs.Equal
|
||||
public val ERROR: Icon = AllIcons.General.Error
|
||||
}
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* 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.highlight
|
||||
|
||||
import com.intellij.execution.impl.ConsoleViewUtil
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.intellij.openapi.editor.ex.util.LexerEditorHighlighter
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleRunner
|
||||
import org.jetbrains.kotlin.console.gutter.ReplIcons
|
||||
|
||||
public class KotlinHistoryHighlighter(private val runner: KotlinConsoleRunner ) {
|
||||
fun addAndHighlightNewCommand(command: String) {
|
||||
val historyEditor = runner.consoleView.historyViewer
|
||||
val historyDocument = historyEditor.document
|
||||
|
||||
if (historyDocument.textLength == 0) { // this will work first time after 'Clear all' action
|
||||
historyDocument.setText("\n")
|
||||
runner.addGutterIndicator(historyEditor, ReplIcons.HISTORY_INDICATOR)
|
||||
}
|
||||
|
||||
val oldHistoryLength = historyDocument.textLength
|
||||
historyDocument.insertString(oldHistoryLength, command)
|
||||
EditorUtil.scrollToTheEnd(historyEditor)
|
||||
|
||||
val inputEditor = runner.consoleView.consoleEditor
|
||||
val lexerHighlighter = inputEditor.highlighter as? LexerEditorHighlighter ?: return
|
||||
val syntaxHighlighter = lexerHighlighter.syntaxHighlighter ?: return
|
||||
|
||||
val lexer = syntaxHighlighter.highlightingLexer
|
||||
lexer.start(command, 0, command.length(), 0)
|
||||
|
||||
val historyMarkupModel = historyEditor.markupModel
|
||||
|
||||
while (true) {
|
||||
val tokenType = lexer.tokenType ?: break
|
||||
val tokenStartOffset = oldHistoryLength + lexer.tokenStart
|
||||
val tokenEndOffset = oldHistoryLength + lexer.tokenEnd
|
||||
|
||||
val contentType = ConsoleViewUtil.getContentTypeForToken(tokenType, syntaxHighlighter)
|
||||
historyMarkupModel.addRangeHighlighter(
|
||||
tokenStartOffset, tokenEndOffset, HighlighterLayer.LAST,
|
||||
contentType.attributes, HighlighterTargetArea.EXACT_RANGE
|
||||
)
|
||||
|
||||
lexer.advance()
|
||||
}
|
||||
}
|
||||
}
|
||||
+138
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.highlight
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfo
|
||||
import com.intellij.codeInsight.daemon.impl.HighlightInfoType
|
||||
import com.intellij.lang.annotation.HighlightSeverity
|
||||
import com.intellij.openapi.command.WriteCommandAction
|
||||
import com.intellij.openapi.editor.colors.CodeInsightColors
|
||||
import com.intellij.openapi.editor.colors.TextAttributesKey
|
||||
import com.intellij.openapi.editor.ex.util.EditorUtil
|
||||
import com.intellij.openapi.editor.markup.HighlighterLayer
|
||||
import com.intellij.openapi.editor.markup.HighlighterTargetArea
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.psi.PsiDocumentManager
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleHistoryManager
|
||||
import org.jetbrains.kotlin.console.KotlinConsoleRunner
|
||||
import org.jetbrains.kotlin.console.SeverityDetails
|
||||
import org.jetbrains.kotlin.console.gutter.KotlinConsoleErrorRenderer
|
||||
import org.jetbrains.kotlin.console.gutter.KotlinConsoleIndicatorRenderer
|
||||
import org.jetbrains.kotlin.console.gutter.ReplIcons
|
||||
import org.jetbrains.kotlin.diagnostics.Severity
|
||||
|
||||
enum class ReplOutputType {
|
||||
USER_OUTPUT,
|
||||
RESULT,
|
||||
INCOMPLETE,
|
||||
ERROR
|
||||
}
|
||||
|
||||
public class KotlinReplOutputHighlighter(
|
||||
private val runner: KotlinConsoleRunner,
|
||||
private val historyManager: KotlinConsoleHistoryManager
|
||||
) {
|
||||
private val consoleView = runner.consoleView
|
||||
private val historyEditor = consoleView.historyViewer
|
||||
private val historyDocument = historyEditor.document
|
||||
private val historyMarkup = historyEditor.markupModel
|
||||
|
||||
private fun resetConsoleEditorIndicator() = runner.changeEditorIndicatorIcon(consoleView.consoleEditor, ReplIcons.EDITOR_INDICATOR)
|
||||
|
||||
private fun insertText(text: String): Pair<Int, Int> {
|
||||
val oldLen = historyDocument.textLength
|
||||
val newLen = oldLen + text.length()
|
||||
|
||||
historyDocument.insertString(oldLen, text)
|
||||
EditorUtil.scrollToTheEnd(historyEditor)
|
||||
|
||||
return oldLen to newLen
|
||||
}
|
||||
|
||||
fun printUserOutput(command: String) {
|
||||
resetConsoleEditorIndicator()
|
||||
historyManager.lastCommandType = ReplOutputType.USER_OUTPUT
|
||||
consoleView.print(command, ReplColors.USER_OUTPUT_CONTENT_TYPE)
|
||||
}
|
||||
|
||||
fun printResultWithGutterIcon(result: String) = WriteCommandAction.runWriteCommandAction(runner.project) {
|
||||
resetConsoleEditorIndicator()
|
||||
historyManager.lastCommandType = ReplOutputType.RESULT
|
||||
|
||||
val (startOffset, endOffset) = insertText(result)
|
||||
|
||||
val highlighter = historyMarkup.addRangeHighlighter(startOffset, endOffset, HighlighterLayer.LAST, null, HighlighterTargetArea.EXACT_RANGE)
|
||||
highlighter.gutterIconRenderer = KotlinConsoleIndicatorRenderer(ReplIcons.RESULT)
|
||||
}
|
||||
|
||||
fun changeIndicatorOnIncomplete() {
|
||||
historyManager.lastCommandType = ReplOutputType.INCOMPLETE
|
||||
runner.changeEditorIndicatorIcon(consoleView.consoleEditor, ReplIcons.INCOMPLETE_INDICATOR)
|
||||
}
|
||||
|
||||
fun highlightErrors(compilerMessages: List<SeverityDetails>) = WriteCommandAction.runWriteCommandAction(runner.project) {
|
||||
resetConsoleEditorIndicator()
|
||||
historyManager.lastCommandType = ReplOutputType.ERROR
|
||||
|
||||
val lastCommandStartOffset = historyDocument.textLength - historyManager.lastCommandLength
|
||||
val highlighterAndMessagesByLine = compilerMessages.filter {
|
||||
it.severity == Severity.ERROR || it.severity == Severity.WARNING
|
||||
}.groupBy { message ->
|
||||
val cmdStart = lastCommandStartOffset + message.range.startOffset
|
||||
historyEditor.document.getLineNumber(cmdStart)
|
||||
}.values().map { messages ->
|
||||
val highlighters = messages.map { message ->
|
||||
val cmdStart = lastCommandStartOffset + message.range.startOffset
|
||||
val cmdEnd = lastCommandStartOffset + message.range.endOffset
|
||||
|
||||
val textAttributes = getAttributesForSeverity(cmdStart, cmdEnd, message.severity)
|
||||
historyMarkup.addRangeHighlighter(
|
||||
cmdStart, cmdEnd, HighlighterLayer.LAST, textAttributes, HighlighterTargetArea.EXACT_RANGE
|
||||
)
|
||||
}
|
||||
Pair(highlighters.first(), messages)
|
||||
}
|
||||
|
||||
for ((highlighter, messages) in highlighterAndMessagesByLine) {
|
||||
highlighter.gutterIconRenderer = KotlinConsoleErrorRenderer(messages)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAttributesForSeverity(start: Int, end: Int, severity: Severity): TextAttributes {
|
||||
val attributes = when (severity) {
|
||||
Severity.ERROR -> getAttributesForSeverity(HighlightInfoType.ERROR, HighlightSeverity.ERROR, CodeInsightColors.ERRORS_ATTRIBUTES, start, end)
|
||||
Severity.WARNING -> getAttributesForSeverity(HighlightInfoType.WARNING, HighlightSeverity.WARNING, CodeInsightColors.WARNINGS_ATTRIBUTES, start, end)
|
||||
Severity.INFO -> getAttributesForSeverity(HighlightInfoType.WEAK_WARNING, HighlightSeverity.WEAK_WARNING, CodeInsightColors.WEAK_WARNING_ATTRIBUTES, start, end)
|
||||
}
|
||||
return attributes
|
||||
}
|
||||
|
||||
private fun getAttributesForSeverity(
|
||||
infoType: HighlightInfoType,
|
||||
severity: HighlightSeverity,
|
||||
insightColors: TextAttributesKey,
|
||||
start: Int,
|
||||
end: Int
|
||||
): TextAttributes {
|
||||
val highlightInfo = HighlightInfo.newHighlightInfo(infoType).range(start, end).severity(severity).textAttributes(insightColors).create()
|
||||
|
||||
val psiFile = PsiDocumentManager.getInstance(runner.project).getPsiFile(runner.consoleView.consoleEditor.document)
|
||||
val colorScheme = runner.consoleView.consoleEditor.colorsScheme
|
||||
|
||||
return highlightInfo?.getTextAttributes(psiFile, colorScheme) ?: TextAttributes()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* 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.highlight
|
||||
|
||||
import com.intellij.execution.ui.ConsoleViewContentType
|
||||
import com.intellij.openapi.editor.markup.TextAttributes
|
||||
import com.intellij.ui.Colors
|
||||
import com.intellij.ui.Gray
|
||||
import com.intellij.ui.JBColor
|
||||
import java.awt.Font
|
||||
|
||||
public object ReplColors {
|
||||
public val HISTORY_GUTTER_COLOR: JBColor = JBColor(Gray.xF2, Gray.x41)
|
||||
public val EDITOR_GUTTER_COLOR: JBColor = JBColor(Gray.xCF, Gray.x31)
|
||||
public val PLACEHOLDER_COLOR: JBColor = JBColor.LIGHT_GRAY
|
||||
public val USER_OUTPUT_CONTENT_TYPE: ConsoleViewContentType =
|
||||
ConsoleViewContentType(
|
||||
"KOTLIN_CONSOLE_USER_OUTPUT",
|
||||
TextAttributes() apply { fontType = Font.ITALIC; foregroundColor = Colors.DARK_GREEN }
|
||||
)
|
||||
}
|
||||
@@ -83,9 +83,9 @@ public class KotlinReplTest : PlatformTestCase() {
|
||||
}
|
||||
|
||||
@Test fun testSimpleCommand() {
|
||||
consoleRunner.submitCommand("1 + 1")
|
||||
val docHistory = checkHistoryUpdate { x: String -> x.endsWith('2') }
|
||||
consoleRunner.pipeline.submitCommand("1 + 1")
|
||||
val docHistory = checkHistoryUpdate { x: String -> x.endsWith("2</output>") }
|
||||
|
||||
assertTrue(docHistory.endsWith('2'), "1 + 1 should be equal 2, but document history is: '$docHistory'")
|
||||
assertTrue(docHistory.endsWith("2</output>"), "1 + 1 should be equal 2, but document history is: '$docHistory'")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user