Refactoring: add composite output handler to avoid multiple forEach calls

This commit is contained in:
Natalia Selezneva
2018-12-11 10:49:20 +03:00
parent 2511f70bd9
commit 392e69c690
3 changed files with 57 additions and 34 deletions
@@ -16,26 +16,54 @@
package org.jetbrains.kotlin.idea.scratch
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutput
import org.jetbrains.kotlin.idea.scratch.output.ScratchOutputHandler
abstract class ScratchExecutor(protected val file: ScratchFile) {
abstract fun execute()
abstract fun stop()
protected val handlers = mutableListOf<ScratchOutputHandler>()
protected val handler = CompositeOutputHandler()
fun addOutputHandler(outputHandler: ScratchOutputHandler) {
handlers.add(outputHandler)
handler.add(outputHandler)
}
fun errorOccurs(message: String, e: Throwable? = null, isFatal: Boolean = false) {
handlers.forEach {
it.error(file, message)
if (isFatal) {
it.onFinish(file)
}
handler.error(file, message)
if (isFatal) {
handler.onFinish(file)
}
if (e != null) LOG.error(e)
}
protected class CompositeOutputHandler : ScratchOutputHandler {
private val handlers = mutableListOf<ScratchOutputHandler>()
fun add(handler: ScratchOutputHandler) {
handlers.add(handler)
}
override fun onStart(file: ScratchFile) {
handlers.forEach { it.onStart(file) }
}
override fun handle(file: ScratchFile, expression: ScratchExpression, output: ScratchOutput) {
handlers.forEach { it.handle(file, expression, output) }
}
override fun error(file: ScratchFile, message: String) {
handlers.forEach { it.error(file, message) }
}
override fun onFinish(file: ScratchFile) {
handlers.forEach { it.onFinish(file) }
}
override fun clear(file: ScratchFile) {
handlers.forEach { it.clear(file) }
}
}
}
@@ -61,7 +61,7 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
private var backgroundProcessIndicator: ProgressIndicator? = null
override fun execute() {
handlers.forEach { it.onStart(file) }
handler.onStart(file)
val module = file.getModule() ?: return error("Module should be selected")
val psiFile = file.getPsiFile() as? KtFile ?: return error("Couldn't find KtFile for current editor")
@@ -108,9 +108,9 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
}
} catch (e: Throwable) {
LOG.info(result.code, e)
handlers.forEach { it.error(file, e.message ?: "Couldn't compile ${psiFile.name}") }
handler.error(file, e.message ?: "Couldn't compile ${psiFile.name}")
} finally {
handlers.forEach { it.onFinish(file) }
handler.onFinish(file)
}
}
}.queue()
@@ -122,7 +122,7 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
try {
backgroundProcessIndicator?.cancel()
} finally {
handlers.forEach { it.onFinish(file) }
handler.onFinish(file)
}
}
@@ -192,14 +192,14 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
try {
AnalyzingUtils.checkForSyntacticErrors(psiFile)
} catch (e: IllegalArgumentException) {
handlers.forEach { it.error(file, e.message ?: "Couldn't compile ${psiFile.name}") }
handler.error(file, e.message ?: "Couldn't compile ${psiFile.name}")
return@runReadAction false
}
val analysisResult = psiFile.analyzeWithAllCompilerChecks()
if (analysisResult.isError()) {
handlers.forEach { it.error(file, analysisResult.error.message ?: "Couldn't compile ${psiFile.name}") }
handler.error(file, analysisResult.error.message ?: "Couldn't compile ${psiFile.name}")
return@runReadAction false
}
@@ -214,16 +214,16 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
val scratchExpression = file.findExpression(diagnostic.psiElement)
if (scratchExpression == null) {
LOG.error("Couldn't find expression to report error: ${diagnostic.psiElement.getElementTextWithContext()}")
handlers.forEach { it.error(file, errorText) }
handler.error(file, errorText)
} else {
handlers.forEach { it.handle(file, scratchExpression, ScratchOutput(errorText, ScratchOutputType.ERROR)) }
handler.handle(file, scratchExpression, ScratchOutput(errorText, ScratchOutputType.ERROR))
}
} else {
handlers.forEach { it.error(file, errorText) }
handler.error(file, errorText)
}
} else {
handlers.forEach { it.error(file, errorText) }
handler.error(file, errorText)
}
}
return@runReadAction false
@@ -233,8 +233,8 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
}
private fun error(message: String) {
handlers.forEach { it.error(file, message) }
handlers.forEach { it.onFinish(file) }
handler.error(file, message)
handler.onFinish(file)
}
private fun ScratchFile.findExpression(psiElement: PsiElement): ScratchExpression? {
@@ -251,7 +251,7 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
val out = processOutput.stdout
val err = processOutput.stderr
if (err.isNotBlank()) {
handlers.forEach { it.error(file, err) }
handler.error(file, err)
}
if (out.isNotBlank()) {
parseStdOut(out)
@@ -282,15 +282,11 @@ class KtCompilingExecutor(file: ScratchFile) : ScratchExecutor(file) {
)
} else {
userOutput.forEach { output ->
handlers.forEach {
it.handle(file, scratchExpression, ScratchOutput(output, ScratchOutputType.OUTPUT))
}
handler.handle(file, scratchExpression, ScratchOutput(output, ScratchOutputType.OUTPUT))
}
results.forEach { result ->
handlers.forEach {
it.handle(file, scratchExpression, ScratchOutput(result, ScratchOutputType.RESULT))
}
handler.handle(file, scratchExpression, ScratchOutput(result, ScratchOutputType.RESULT))
}
}
@@ -40,7 +40,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
private lateinit var osProcessHandler: OSProcessHandler
override fun execute() {
handlers.forEach { it.onStart(file) }
handler.onStart(file)
val module = file.getModule() ?: return error("Module should be selected")
val cmdLine = KotlinConsoleKeeper.createCommandLine(module)
@@ -62,7 +62,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
try {
osProcessHandler.process.destroy()
} finally {
handlers.forEach { it.onFinish(file) }
handler.onFinish(file)
}
}
@@ -80,8 +80,8 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
}
private fun error(file: ScratchFile, message: String) {
handlers.forEach { it.error(file, message) }
handlers.forEach { it.onFinish(file) }
handler.error(file, message)
handler.onFinish(file)
}
private class ReplHistory {
@@ -118,7 +118,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
}
override fun notifyProcessTerminated(exitCode: Int) {
handlers.forEach { it.onFinish(file) }
handler.onFinish(file)
}
private fun strToSource(s: String, encoding: Charset = Charsets.UTF_8) = InputSource(ByteArrayInputStream(s.toByteArray(encoding)))
@@ -128,8 +128,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
val output = try {
factory.newDocumentBuilder().parse(strToSource(text))
} catch (e: Exception) {
handlers.forEach { it.error(file, "Couldn't parse REPL output: $text") }
return
return handler.error(file, "Couldn't parse REPL output: $text")
}
val root = output.firstChild as Element
@@ -152,7 +151,7 @@ class KtScratchReplExecutor(file: ScratchFile) : ScratchExecutor(file) {
}
if (lastExpression != null) {
handlers.forEach { it.handle(file, lastExpression, result) }
handler.handle(file, lastExpression, result)
}
}
}