[ide-console] Move warning messages about compilation inside consoleView

This commit is contained in:
Dmitry Kovanikov
2015-08-28 16:02:37 +03:00
committed by Pavel V. Talanov
parent 058b539004
commit fcc5fa13f3
8 changed files with 112 additions and 79 deletions
@@ -0,0 +1,50 @@
/*
* 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.ExecutionManager
import com.intellij.execution.Executor
import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.openapi.compiler.CompileContext
import com.intellij.openapi.compiler.CompilerManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
public class KotlinConsoleCompilerHelper(
private val project: Project,
private val module: Module,
private val executor: Executor,
private val contentDescriptor: RunContentDescriptor
) {
fun moduleIsUpToDate(): Boolean {
val compilerManager = CompilerManager.getInstance(project)
val compilerScope = compilerManager.createModuleCompileScope(module, true)
return compilerManager.isUpToDate(compilerScope)
}
fun compileModule() {
if (ExecutionManager.getInstance(project).contentManager.removeRunContent(executor, contentDescriptor)) {
CompilerManager.getInstance(project).make(module) {
aborted: Boolean, errors: Int, warnings: Int, compileContext: CompileContext ->
if (!module.isDisposed) {
KotlinConsoleKeeper.getInstance(project).run(module, previousCompilationFailed = errors > 0)
}
}
}
}
}
@@ -44,11 +44,11 @@ public class KotlinConsoleKeeper(val project: Project) {
fun putVirtualFileToConsole(virtualFile: VirtualFile, console: KotlinConsoleRunner) = consoleMap.put(virtualFile, console)
fun removeConsole(virtualFile: VirtualFile) = consoleMap.remove(virtualFile)
fun run(module: Module, previousCompilationFailed: Boolean = false, testMode: Boolean = false): KotlinConsoleRunner? {
fun run(module: Module, testMode: Boolean = false, previousCompilationFailed: Boolean = false): KotlinConsoleRunner? {
val path = module.moduleFilePath
val cmdLine = createCommandLine(module) ?: return errorNotification(project, "Module SDK not found") let { null }
val consoleRunner = KotlinConsoleRunner(cmdLine, module, previousCompilationFailed, testMode, project, REPL_TITLE, path)
val consoleRunner = KotlinConsoleRunner(module, cmdLine, testMode, previousCompilationFailed, project, REPL_TITLE, path)
consoleRunner.initAndRun()
consoleRunner.setupGutters()
@@ -30,7 +30,6 @@ import com.intellij.openapi.editor.markup.*
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import com.intellij.openapi.util.Disposer
import com.intellij.util.Consumer
import org.jetbrains.kotlin.console.actions.BuildAndRestartConsoleAction
import org.jetbrains.kotlin.console.actions.KtExecuteCommandAction
import org.jetbrains.kotlin.console.gutter.KotlinConsoleGutterContentProvider
@@ -46,20 +45,21 @@ import javax.swing.Icon
import kotlin.properties.Delegates
public class KotlinConsoleRunner(
val module: Module,
private val cmdLine: GeneralCommandLine,
private val module: Module,
private val previousCompilationFailed: Boolean,
private val testMode: Boolean,
private val previousCompilationFailed: Boolean,
myProject: Project,
title: String,
path: String?
) : AbstractConsoleRunnerWithHistory<LanguageConsoleView>(myProject, title, path) {
private val editorToIndicator = ConcurrentHashMap<EditorEx, RangeHighlighter>()
private val historyManager = KotlinConsoleHistoryManager(this)
val executor = KotlinConsoleExecutor(this, historyManager)
private var disposableDescriptor: RunContentDescriptor by Delegates.notNull()
val executor = KotlinConsoleExecutor(this, historyManager)
var compilerHelper: KotlinConsoleCompilerHelper by Delegates.notNull()
override fun createProcess() = cmdLine.createProcess()
override fun createConsoleView(): LanguageConsoleView? {
@@ -81,7 +81,9 @@ public class KotlinConsoleRunner(
}
override fun createProcessHandler(process: Process): OSProcessHandler {
val processHandler = KotlinReplOutputHandler(KotlinReplOutputHighlighter(this, historyManager), process, cmdLine.commandLineString)
val processHandler = KotlinReplOutputHandler(
KotlinReplOutputHighlighter(this, historyManager, testMode, previousCompilationFailed), process, cmdLine.commandLineString
)
val consoleFile = consoleView.virtualFile
val keeper = KotlinConsoleKeeper.getInstance(project)
@@ -104,9 +106,10 @@ public class KotlinConsoleRunner(
contentDescriptor: RunContentDescriptor
): List<AnAction> {
disposableDescriptor = contentDescriptor
compilerHelper = KotlinConsoleCompilerHelper(project, module, defaultExecutor, contentDescriptor)
val actionList = arrayListOf<AnAction>(
BuildAndRestartConsoleAction(project, module, defaultExecutor, contentDescriptor, previousCompilationFailed, testMode),
BuildAndRestartConsoleAction(this),
createConsoleExecAction(consoleExecuteActionHandler),
createCloseAction(defaultExecutor, contentDescriptor)
)
@@ -42,6 +42,7 @@ public class KotlinReplOutputHandler(
commandLine: String
) : OSProcessHandler(process, commandLine) {
private var isBuildInfoChecked = false
private val dBuilder = DocumentBuilderFactory.newInstance().newDocumentBuilder()
override fun isSilentlyDestroyOnClose() = true
@@ -56,7 +57,7 @@ public class KotlinReplOutputHandler(
val content = StringUtil.replace(root.textContent, XML_REPLACEMENTS, SOURCE_CHARS)
when (outputType) {
"INITIAL_PROMPT" -> outputHighlighter.printInitialPrompt(content)
"INITIAL_PROMPT" -> buildWarningIfNeededBeforeInit(content)
"HELP_PROMPT" -> outputHighlighter.printHelp(content)
"USER_OUTPUT" -> outputHighlighter.printUserOutput(content)
"REPL_RESULT" -> outputHighlighter.printResultWithGutterIcon(content)
@@ -67,6 +68,14 @@ public class KotlinReplOutputHandler(
}
}
private fun buildWarningIfNeededBeforeInit(content: String) {
if (!isBuildInfoChecked) {
outputHighlighter.printBuildInfoWarningIfNeeded()
isBuildInfoChecked = true
}
outputHighlighter.printInitialPrompt(content)
}
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
private fun createCompilerMessages(runtimeErrorsReport: String): List<SeverityDetails> {
@@ -16,76 +16,14 @@
package org.jetbrains.kotlin.console.actions
import com.intellij.execution.ExecutionManager
import com.intellij.execution.Executor
import com.intellij.execution.ui.RunContentDescriptor
import com.intellij.icons.AllIcons
import com.intellij.notification.Notification
import com.intellij.notification.NotificationListener
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.compiler.CompileContext
import com.intellij.openapi.compiler.CompilerManager
import com.intellij.openapi.module.Module
import com.intellij.openapi.project.Project
import org.jetbrains.kotlin.console.KotlinConsoleKeeper
import javax.swing.event.HyperlinkEvent
private fun buildAndRestartMessage(module: Module) = "Build module '${module.name}' and restart"
import org.jetbrains.kotlin.console.KotlinConsoleRunner
public class BuildAndRestartConsoleAction(
private val project: Project,
private val module: Module,
private val executor: Executor,
private val contentDescriptor: RunContentDescriptor,
private val previousCompilationFailed: Boolean,
private val testMode: Boolean
) : AnAction("Build and restart", buildAndRestartMessage(module), AllIcons.Actions.Restart) {
private val runner: KotlinConsoleRunner
) : AnAction("Build and restart", "Build module '${runner.module.name}' and restart", AllIcons.Actions.Restart) {
init {
if (!testMode && !previousCompilationFailed) showOutdatedClassesNotificationIfNeeded()
}
override fun actionPerformed(_: AnActionEvent) = compileModule()
private fun compileModule() {
if (ExecutionManager.getInstance(project).contentManager.removeRunContent(executor, contentDescriptor)) {
CompilerManager.getInstance(project).make(module) {
aborted: Boolean, errors: Int, warnings: Int, compileContext: CompileContext ->
if (!module.isDisposed) {
val compilationFailed = aborted || errors > 0
if (compilationFailed) warningNotification("Compilation wasn't ended properly")
KotlinConsoleKeeper.getInstance(project).run(module, previousCompilationFailed = compilationFailed)
}
}
}
}
private fun showOutdatedClassesNotificationIfNeeded() {
val compilerManager = CompilerManager.getInstance(project)
val compilerScope = compilerManager.createModuleCompileScope(module, true)
if (compilerManager.isUpToDate(compilerScope)) return
val message = "Youre running the REPL with outdated classes<br><a href=\"Build and restart\">${buildAndRestartMessage(module)}</a>"
val hyperlinkListener = object : NotificationListener {
override fun hyperlinkUpdate(notification: Notification, event: HyperlinkEvent) {
if (event.eventType == HyperlinkEvent.EventType.ACTIVATED) {
compileModule()
notification.expire()
}
}
}
warningNotification(message, hyperlinkListener)
}
private fun warningNotification(message: String, hyperlinkListener: NotificationListener? = null) {
val warningTag = "KOTLIN REPL WARNING"
val warningTitle = "Kotlin REPL Configuration Warning"
Notifications.Bus.notify(Notification(warningTag, warningTitle, message, NotificationType.WARNING, hyperlinkListener), project)
}
override fun actionPerformed(_: AnActionEvent) = runner.compilerHelper.compileModule()
}
@@ -21,6 +21,7 @@ import org.jetbrains.kotlin.idea.JetIcons
import javax.swing.Icon
public object ReplIcons {
public val BUILD_WARNING_INDICATOR: Icon = AllIcons.General.Information
public val HISTORY_INDICATOR: Icon = AllIcons.General.MessageHistory
public val EDITOR_INDICATOR: Icon = JetIcons.LAUNCH
public val INCOMPLETE_INDICATOR: Icon = AllIcons.Nodes.Desktop
@@ -47,7 +47,9 @@ enum class ReplOutputType {
public class KotlinReplOutputHighlighter(
private val runner: KotlinConsoleRunner,
private val historyManager: KotlinConsoleHistoryManager
private val historyManager: KotlinConsoleHistoryManager,
private val testMode: Boolean,
private val previousCompilationFailed: Boolean
) {
private val project = runner.project
private val consoleView = runner.consoleView as LanguageConsoleImpl
@@ -65,17 +67,42 @@ public class KotlinReplOutputHighlighter(
return Pair(oldLen, newLen)
}
private fun printOutput(output: String, contentType: ConsoleViewContentType, icon: Icon) {
private fun printOutput(output: String, contentType: ConsoleViewContentType, icon: Icon? = null) {
val (startOffset, endOffset) = textOffsets(output)
consoleView.print(output, contentType)
consoleView.flushDeferredText()
if (icon == null) return
historyMarkup.addRangeHighlighter(
startOffset, endOffset, HighlighterLayer.LAST, null, HighlighterTargetArea.EXACT_RANGE
) apply { gutterIconRenderer = KotlinConsoleIndicatorRenderer(icon) }
}
private fun printWarningMessage(message: String, isAddHyperlink: Boolean) = WriteCommandAction.runWriteCommandAction(project) {
printOutput("\n", ConsoleViewContentType.NORMAL_OUTPUT)
printOutput(message, ReplColors.WARNING_INFO_CONTENT_TYPE, ReplIcons.BUILD_WARNING_INDICATOR)
if (isAddHyperlink) {
consoleView.printHyperlink("Build module '${runner.module.name}' and restart") {
runner.compilerHelper.compileModule()
}
}
printOutput("\n\n", ConsoleViewContentType.NORMAL_OUTPUT)
}
fun printBuildInfoWarningIfNeeded() {
if (testMode) return
if (previousCompilationFailed) return printWarningMessage("There were compilation errors in module ${runner.module.name}", false)
if (runner.compilerHelper.moduleIsUpToDate()) return
val compilerWarningMessage = "Youre running the REPL with outdated classes: "
printWarningMessage(compilerWarningMessage, true)
}
fun printInitialPrompt(command: String) = consoleView.print(command, ReplColors.INITIAL_PROMPT_CONTENT_TYPE)
fun printHelp(help: String) = WriteCommandAction.runWriteCommandAction(project) {
@@ -105,7 +132,7 @@ public class KotlinReplOutputHighlighter(
// remove line breaks after incomplete part
val historyText = historyDocument.text
val historyLength = historyText.length()
val trimmedHistoryText = historyText.trim()
val trimmedHistoryText = historyText.trimEnd()
val whitespaceCharsToDelete = historyLength - trimmedHistoryText.length()
historyDocument.deleteString(historyLength - whitespaceCharsToDelete, historyLength)
@@ -28,6 +28,11 @@ public object ReplColors {
public val EDITOR_GUTTER_COLOR: JBColor = JBColor(Gray.xCF, Gray.x31)
public val PLACEHOLDER_COLOR: JBColor = JBColor.LIGHT_GRAY
public val WARNING_INFO_CONTENT_TYPE: ConsoleViewContentType =
ConsoleViewContentType(
"KOTLIN_CONSOLE_WARNING_INFO",
TextAttributes() apply { fontType = Font.ITALIC; foregroundColor = JBColor.RED }
)
public val INITIAL_PROMPT_CONTENT_TYPE: ConsoleViewContentType =
ConsoleViewContentType(
"KOTLIN_CONSOLE_INITIAL_PROMPT",