com.jetbrains.pluginverifier.output.stream.WriterResultPrinter.kt Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of verifier-cli Show documentation
Show all versions of verifier-cli Show documentation
Command-line interface for JetBrains Plugin Verifier with set of high-level tasks for plugin and IDE validation
/*
* Copyright 2000-2020 JetBrains s.r.o. and other contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
*/
package com.jetbrains.pluginverifier.output.stream
import com.jetbrains.plugin.structure.base.problems.PluginProblem
import com.jetbrains.plugin.structure.base.problems.isError
import com.jetbrains.plugin.structure.intellij.problems.remapping.ignored.CliIgnoredProblemLevelRemappingManager
import com.jetbrains.pluginverifier.PluginVerificationResult
import com.jetbrains.pluginverifier.dymamic.DynamicPluginStatus
import com.jetbrains.pluginverifier.dymamic.DynamicPlugins
import com.jetbrains.pluginverifier.output.DYNAMIC_PLUGIN_FAIL
import com.jetbrains.pluginverifier.output.DYNAMIC_PLUGIN_PASS
import com.jetbrains.pluginverifier.output.ResultPrinter
import com.jetbrains.pluginverifier.tasks.InvalidPluginFile
import java.io.PrintWriter
import java.util.*
class WriterResultPrinter(private val out: PrintWriter) : ResultPrinter {
private companion object {
private const val INDENT = " "
}
private val problemSolutionHintProvider = CliIgnoredProblemLevelRemappingManager()
override fun printResults(results: List) {
results.forEach { result ->
val plugin = result.plugin
val verificationTarget = result.verificationTarget
out.println("Plugin $plugin against $verificationTarget: ${result.verificationVerdict}")
if (result is PluginVerificationResult.Verified) {
out.println(result.printVerificationResult())
}
}
}
fun printInvalidPluginFiles(invalidPluginFiles: List) {
if (invalidPluginFiles.isNotEmpty()) {
out.println("The following files specified for the verification are not valid plugins:")
for ((pluginFile, pluginProblems) in invalidPluginFiles) {
out.println(" $pluginFile")
val (pluginErrors, otherPluginProblems) = pluginProblems.partition { it.isError }
if (pluginErrors.isNotEmpty()) {
out.println(" Plugin problems:")
for (pluginError in pluginErrors) {
out.println(" $pluginError")
pluginError.printProblemSolutionHint(indentLevel = 2)
}
}
if (otherPluginProblems.isNotEmpty()) {
out.println(" Additional plugin warnings:")
for (pluginProblem in otherPluginProblems) {
out.println(" $pluginProblem")
pluginProblem.printProblemSolutionHint(indentLevel = 2)
}
}
}
}
}
private fun PluginProblem.printProblemSolutionHint(indentLevel: Int = 1) {
if (solutionHint.isEmpty()) return
val indent = " ".repeat(indentLevel * 4)
out.println(" $indent$solutionHint")
}
private fun PluginVerificationResult.Verified.printVerificationResult(): String = buildString {
if (pluginStructureWarnings.isNotEmpty()) {
appendLine("Plugin structure warnings (${pluginStructureWarnings.size}): ")
for (warning in pluginStructureWarnings) {
appendLine("$INDENT${warning.message}")
warning.problem.solutionHint.takeUnless { it.isBlank() }?.let { appendLine(INDENT + INDENT + it) }
}
}
val directMissingDependencies = dependenciesGraph.getDirectMissingDependencies()
if (directMissingDependencies.isNotEmpty()) {
appendLine("Missing dependencies: ")
for (missingDependency in directMissingDependencies) {
appendLine("$INDENT${missingDependency.dependency}: ${missingDependency.missingReason}")
}
}
if (compatibilityWarnings.isNotEmpty()) {
appendLine("Compatibility warnings (${compatibilityWarnings.size}): ")
appendShortAndFullDescriptions(compatibilityWarnings.groupBy({ it.shortDescription }, { it.fullDescription }))
}
if (compatibilityProblems.isNotEmpty()) {
appendLine("Compatibility problems (${compatibilityProblems.size}): ")
appendShortAndFullDescriptions(compatibilityProblems.groupBy({ it.shortDescription }, { it.fullDescription }))
}
if (deprecatedUsages.isNotEmpty()) {
appendLine("Deprecated API usages (${deprecatedUsages.size}): ")
appendShortAndFullDescriptions(deprecatedUsages.groupBy({ it.shortDescription }, { it.fullDescription }))
}
if (experimentalApiUsages.isNotEmpty()) {
appendLine("Experimental API usages (${experimentalApiUsages.size}): ")
appendShortAndFullDescriptions(experimentalApiUsages.groupBy({ it.shortDescription }, { it.fullDescription }))
}
if (internalApiUsages.isNotEmpty()) {
appendLine("Internal API usages (${internalApiUsages.size}): ")
appendShortAndFullDescriptions(internalApiUsages.groupBy({ it.shortDescription }, { it.fullDescription }))
}
if (overrideOnlyMethodUsages.isNotEmpty()) {
appendLine("Override-only API usages (${overrideOnlyMethodUsages.size}): ")
appendShortAndFullDescriptions(overrideOnlyMethodUsages.groupBy({ it.shortDescription }, { it.fullDescription }))
}
if (nonExtendableApiUsages.isNotEmpty()) {
appendLine("Non-extendable API usages (${nonExtendableApiUsages.size}): ")
appendShortAndFullDescriptions(nonExtendableApiUsages.groupBy({ it.shortDescription }, { it.fullDescription }))
}
val dynamicPluginStatusHeading = "Dynamic Plugin Eligibility"
when (val dynamicPluginStatus = dynamicPluginStatus) {
is DynamicPluginStatus.MaybeDynamic -> appendLine("$dynamicPluginStatusHeading:").appendLine(INDENT + DYNAMIC_PLUGIN_PASS)
is DynamicPluginStatus.NotDynamic -> {
val restrictions = dynamicPluginStatus.shortToFullDescriptions()
val heading = "$dynamicPluginStatusHeading (negative due to ${restrictions.size} restrictions):"
appendLine(heading).appendLine(INDENT + DYNAMIC_PLUGIN_FAIL)
appendShortAndFullDescriptions(restrictions)
}
null -> Unit
}
}
private fun DynamicPluginStatus.NotDynamic.shortToFullDescriptions(): Map> {
val justReasonRestrictions = reasonsNotToLoadUnloadWithoutRestart.map { reason ->
reason.removePrefix(DynamicPlugins.MESSAGE + " because ")
.replaceFirstChar { if (it.isLowerCase()) it.titlecase(Locale.getDefault()) else it.toString() }
}
return justReasonRestrictions.associateWith { emptyList() }
}
private fun StringBuilder.appendShortAndFullDescriptions(shortToFullDescriptions: Map>) {
val indent = " "
shortToFullDescriptions.forEach { (shortDescription, fullDescriptions) ->
appendLine("$indent#$shortDescription")
for (fullDescription in fullDescriptions) {
fullDescription.lines().forEach { line ->
appendLine("$indent$indent$line")
}
}
}
}
private val PluginProblem.solutionHint: String
get() = problemSolutionHintProvider.getProblemSolutionHint(this) ?: ""
}