All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.jetbrains.pluginverifier.output.markdown.MarkdownResultPrinter.kt Maven / Gradle / Ivy

Go to download

Command-line interface for JetBrains Plugin Verifier with set of high-level tasks for plugin and IDE validation

There is a newer version: 1.379
Show newest version
package com.jetbrains.pluginverifier.output.markdown

import com.jetbrains.plugin.structure.base.problems.isError
import com.jetbrains.plugin.structure.base.utils.create
import com.jetbrains.pluginverifier.PluginVerificationResult
import com.jetbrains.pluginverifier.PluginVerificationTarget
import com.jetbrains.pluginverifier.dependencies.MissingDependency
import com.jetbrains.pluginverifier.dymamic.DynamicPluginStatus
import com.jetbrains.pluginverifier.dymamic.DynamicPlugins.simplifiedReasonsNotToLoadUnloadWithoutRestart
import com.jetbrains.pluginverifier.output.DYNAMIC_PLUGIN_FAIL
import com.jetbrains.pluginverifier.output.OutputOptions
import com.jetbrains.pluginverifier.output.ResultPrinter
import com.jetbrains.pluginverifier.results.problems.CompatibilityProblem
import com.jetbrains.pluginverifier.tasks.InvalidPluginFile
import com.jetbrains.pluginverifier.usages.deprecated.DeprecatedApiUsage
import com.jetbrains.pluginverifier.usages.experimental.ExperimentalApiUsage
import com.jetbrains.pluginverifier.usages.internal.InternalApiUsage
import com.jetbrains.pluginverifier.usages.nonExtendable.NonExtendableApiUsage
import com.jetbrains.pluginverifier.usages.overrideOnly.OverrideOnlyMethodUsage
import com.jetbrains.pluginverifier.warnings.CompatibilityWarning
import com.jetbrains.pluginverifier.warnings.PluginStructureWarning
import java.io.PrintWriter
import java.nio.file.Files

private const val REPORT_FILE_NAME = "report.md"

class MarkdownResultPrinter(private val out: PrintWriter) : ResultPrinter, AutoCloseable {

  companion object {
    fun create(verificationTarget: PluginVerificationTarget, outputOptions: OutputOptions): MarkdownResultPrinter {
      val reportHtmlFile = outputOptions.getTargetReportDirectory(verificationTarget).resolve(REPORT_FILE_NAME)
      val writer = PrintWriter(Files.newBufferedWriter(reportHtmlFile.create()))
      return MarkdownResultPrinter(writer)
    }
  }

  override fun printResults(results: List) {
    markdown(out) {
      results.forEach {
        printResult(it, this)
      }
    }
  }

  fun printInvalidPluginFiles(invalidPluginFiles: List) {
    markdown(out) {
      when (invalidPluginFiles.size) {
        0 -> return@markdown
        1 -> {
          h1("Invalid plugin")
          paragraph("The following file specified for the verification is not a valid plugin.")
        }
        else -> {
          h1("Invalid plugins")
          out.println("The following files specified for the verification are not valid plugins.")
        }
      }
      if (invalidPluginFiles.isNotEmpty()) {
        for ((pluginFile, pluginProblems) in invalidPluginFiles) {
          h2("${pluginFile.fileName}")
          paragraph("Full path: `$pluginFile`")
          val (pluginErrors, otherPluginProblems) = pluginProblems.partition { it.isError }
          if (pluginErrors.isNotEmpty()) {
            h3("Plugin Problems")
            for (pluginError in pluginErrors) {
              unorderedListItem("$pluginError")
            }
            unorderedListEnd()
          }
          if (otherPluginProblems.isNotEmpty()) {
            val prefix = if (pluginErrors.isEmpty()) "" else "Additional "

            h3("${prefix}Plugin Warnings")
            for (pluginProblem in otherPluginProblems) {
              unorderedListItem("$pluginProblem")
            }
            unorderedListEnd()
          }
        }
      }
    }
  }

  private fun printResult(pluginVerificationResult: PluginVerificationResult, markdown: Markdown) {
    with(pluginVerificationResult) {
      markdown.h1("Plugin $plugin against $verificationTarget")
      markdown.paragraph(verificationVerdict)
      if (this is PluginVerificationResult.Verified) {
        markdown + this
      }
    }
  }

  override fun close() {
    out.close()
  }

}

fun markdown(out: PrintWriter, init: Markdown.() -> Unit): Markdown {
  val markdown = Markdown(out)
  markdown.init()
  return markdown
}

class Markdown(private val out: PrintWriter) {
  fun h1(header: String) {
    out.println("# $header")
    out.println()
  }

  fun h2(header: String): Markdown {
    out.println("## $header")
    out.println()
    return this
  }

  fun h3(header: String) {
    out.println("### $header")
    out.println()
  }

  fun unorderedListItem(content: String) {
    out.println("* $content")
  }

  fun unorderedListEnd() {
    out.println()
  }

  fun paragraph(content: String): Markdown {
    out.println(content)
    out.appendLine()
    return this
  }

  operator fun String.unaryPlus() {
    out.println(this)
  }

  operator fun plus(value: String) {
    out.println(value)
  }

  operator fun plus(@Suppress("UNUSED_PARAMETER") markdown: Markdown) {
    // no-op. Concatenating two Markdown instances is equivalent to concatenating their Writers
  }
}

private operator fun Markdown.plus(result: PluginVerificationResult.Verified) {
  printVerificationResult(result)
}

private fun Markdown.printVerificationResult(result: PluginVerificationResult.Verified) = with(result) {
  printVerificationResult("Plugin structure warnings", pluginStructureWarnings, PluginStructureWarning::describe)
  printVerificationResult("Missing dependencies", dependenciesGraph.getDirectMissingDependencies(), MissingDependency::describe)
  printVerificationResult("Compatibility warnings", compatibilityWarnings, CompatibilityWarning::describe)
  printVerificationResult("Compatibility problems", compatibilityProblems, CompatibilityProblem::describe)
  printVerificationResult("Deprecated API usages", deprecatedUsages, DeprecatedApiUsage::describe)
  printVerificationResult("Experimental API usages", experimentalApiUsages, ExperimentalApiUsage::describe)
  printVerificationResult("Internal API usages", internalApiUsages, InternalApiUsage::describe)
  printVerificationResult("Override-only API usages", overrideOnlyMethodUsages, OverrideOnlyMethodUsage::describe)
  printVerificationResult("Non-extendable API usages", nonExtendableApiUsages, NonExtendableApiUsage::describe)

  val dynaStatus = "Dynamic Plugin Status"
  when (val dynamicPluginStatus = dynamicPluginStatus) {
    is DynamicPluginStatus.MaybeDynamic -> h2(dynaStatus) + paragraph("Plugin can probably be enabled or disabled without IDE restart")
    is DynamicPluginStatus.NotDynamic -> {
      h2(dynaStatus) +
        paragraph(DYNAMIC_PLUGIN_FAIL)
        dynamicPluginStatus.simplifiedReasonsNotToLoadUnloadWithoutRestart().forEach {
          unorderedListItem(it)
        }
        unorderedListEnd()
    }
    null -> Unit
  }
}

private fun  Markdown.printVerificationResult(title: String,
                                                 items: Set, descriptionPropertyExtractor: (T) -> Pair) {
  if (items.isNotEmpty()) {
    h2("$title (${items.size})")
    val shortToFullDescriptions = items.map(descriptionPropertyExtractor)
      .groupBy({ it.first }, { it.second })
    appendShortAndFullDescriptions(shortToFullDescriptions)
  }
}

private fun Markdown.appendShortAndFullDescriptions(shortToFullDescriptions: Map>) {
  shortToFullDescriptions.forEach { (shortDescription, fullDescriptions) ->
    if (shortDescription.isNotEmpty()) {
      h3(shortDescription)
    }
    for (fullDescription in fullDescriptions) {
      fullDescription.lines().forEach { line ->
        unorderedListItem(line)
      }
    }
    unorderedListEnd()
  }
}

fun PluginStructureWarning.describe() = "" to message
fun MissingDependency.describe() = "" to "$dependency: $missingReason"
fun CompatibilityWarning.describe() = shortDescription to fullDescription
fun CompatibilityProblem.describe() = shortDescription to fullDescription
fun DeprecatedApiUsage.describe() = shortDescription to fullDescription
fun ExperimentalApiUsage.describe() = shortDescription to fullDescription
fun InternalApiUsage.describe() = shortDescription to fullDescription
fun OverrideOnlyMethodUsage.describe() = shortDescription to fullDescription
fun NonExtendableApiUsage.describe() = shortDescription to fullDescription




© 2015 - 2024 Weber Informatics LLC | Privacy Policy