MissingRecentAPI inspection: provide a setting to specify actual [since; until] compatibility range.

This commit is contained in:
Sergey Patrikeev
2019-02-05 20:04:48 +03:00
parent a82ac50dad
commit 824b9a9672
3 changed files with 103 additions and 4 deletions
@@ -0,0 +1,18 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.devkit.inspections.missingApi
import com.intellij.openapi.util.BuildNumber
import com.intellij.ui.components.JBTextField
/**
* Text field used to input build numbers.
*/
class BuildNumberField(valueName: String, defaultValue: BuildNumber) : JBTextField() {
val valueEditor: BuildNumberValueEditor = BuildNumberValueEditor(this, valueName, defaultValue)
var value: BuildNumber
get() = valueEditor.value
set(value) {
valueEditor.value = value
}
}
@@ -0,0 +1,26 @@
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
package org.jetbrains.idea.devkit.inspections.missingApi
import com.intellij.openapi.util.BuildNumber
import com.intellij.openapi.util.InvalidDataException
import com.intellij.ui.components.fields.valueEditors.TextFieldValueEditor
import javax.swing.JTextField
/**
* [TextFieldValueEditor] for [BuildNumber]s that can be used to validate user-inputed build numbers.
*/
class BuildNumberValueEditor(buildField: JTextField, valueName: String?, defaultValue: BuildNumber)
: TextFieldValueEditor<BuildNumber>(buildField, valueName, defaultValue) {
override fun parseValue(text: String?): BuildNumber {
if (text.isNullOrBlank()) {
return defaultValue
}
return BuildNumber.fromStringOrNull(text)
?: throw InvalidDataException("Invalid build number: $text")
}
override fun valueToString(value: BuildNumber) = value.asString()
override fun isValid(value: BuildNumber) = true
}
@@ -10,10 +10,15 @@ import com.intellij.openapi.module.ModuleUtil
import com.intellij.openapi.util.BuildNumber
import com.intellij.psi.PsiElementVisitor
import com.intellij.psi.xml.XmlFile
import com.intellij.ui.components.JBLabel
import com.intellij.util.ui.FormBuilder
import org.jetbrains.idea.devkit.actions.DevkitActionsUtil
import org.jetbrains.idea.devkit.module.PluginModuleType
import org.jetbrains.idea.devkit.util.DescriptorUtil
import org.jetbrains.idea.devkit.util.PsiUtil
import java.awt.BorderLayout
import javax.swing.JComponent
import javax.swing.JPanel
/**
* Inspection that warns plugin authors if they use IntelliJ Platform's APIs that aren't available
@@ -36,6 +41,26 @@ class MissingRecentApiInspection : LocalInspectionTool() {
val INSPECTION_SHORT_NAME = InspectionProfileEntry.getShortName(MissingRecentApiInspection::class.java.simpleName)
}
/**
* Actual "since" build constraint of the plugin under development.
*
* Along with [untilBuildString] it may be set manually if values in plugin.xml
* differ from the actual values. For example, it is the case for gradle-intellij-plugin,
* which allows to override "since" and "until" values during plugin build.
*/
var sinceBuildString: String? = null
/**
* Actual "until" build constraint of the plugin under development.
*/
var untilBuildString: String? = null
private val sinceBuild: BuildNumber?
get() = sinceBuildString?.let { BuildNumber.fromStringOrNull(it) }
private val untilBuild: BuildNumber?
get() = untilBuildString?.let { BuildNumber.fromStringOrNull(it) }
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean): PsiElementVisitor {
if (PsiUtil.isIdeaProject(holder.project)) {
return PsiElementVisitor.EMPTY_VISITOR
@@ -48,11 +73,41 @@ class MissingRecentApiInspection : LocalInspectionTool() {
return MissingRecentApiVisitor(holder, ProblemHighlightType.GENERIC_ERROR_OR_WARNING, targetedSinceUntilRanges)
}
override fun createOptionsPanel(): JComponent {
val emptyBuildNumber = BuildNumber.fromString("1.0")
val sinceField = BuildNumberField("since", emptyBuildNumber)
sinceBuild?.also { sinceField.value = it }
sinceField.emptyText.text = "the first IDE"
sinceField.valueEditor.addListener { value ->
sinceBuildString = value.takeIf { it != emptyBuildNumber }?.asString()
}
val untilField = BuildNumberField("until", untilBuild ?: emptyBuildNumber)
untilField.emptyText.text = "all future IDEs"
untilBuild?.also { untilField.value = it }
untilField.valueEditor.addListener { value ->
untilBuildString = value.takeIf { it != emptyBuildNumber }?.asString()
}
val formBuilder = FormBuilder.createFormBuilder()
.addComponent(JBLabel("Plugin compatibility range"))
.addLabeledComponent("since", sinceField)
.addLabeledComponent("until", untilField)
val container = JPanel(BorderLayout())
container.add(formBuilder.panel, BorderLayout.NORTH)
return container
}
private fun getTargetedSinceUntilRanges(module: Module): List<SinceUntilRange> {
return DevkitActionsUtil.getCandidatePluginModules(module)
.mapNotNull { PluginModuleType.getPluginXml(it) }
.mapNotNull { getSinceUntilRange(it) }
.filterNot { it.sinceBuild == null }
if (sinceBuild == null && untilBuild == null) {
return DevkitActionsUtil.getCandidatePluginModules(module)
.mapNotNull { PluginModuleType.getPluginXml(it) }
.mapNotNull { getSinceUntilRange(it) }
.filterNot { it.sinceBuild == null }
}
return listOf(SinceUntilRange(sinceBuild, untilBuild))
}
private fun getSinceUntilRange(pluginXml: XmlFile): SinceUntilRange? {