|
|
|
@@ -5,21 +5,30 @@
|
|
|
|
|
|
|
|
|
|
package org.jetbrains.kotlin.idea.inspections
|
|
|
|
|
|
|
|
|
|
import com.intellij.codeInspection.LocalInspectionToolSession
|
|
|
|
|
import com.intellij.codeInspection.ProblemsHolder
|
|
|
|
|
import com.intellij.analysis.AnalysisScope
|
|
|
|
|
import com.intellij.codeInspection.*
|
|
|
|
|
import com.intellij.codeInspection.reference.RefEntity
|
|
|
|
|
import com.intellij.codeInspection.reference.RefFile
|
|
|
|
|
import com.intellij.codeInspection.reference.RefPackage
|
|
|
|
|
import com.intellij.openapi.editor.event.DocumentAdapter
|
|
|
|
|
import com.intellij.openapi.editor.event.DocumentEvent
|
|
|
|
|
import com.intellij.openapi.ui.LabeledComponent
|
|
|
|
|
import com.intellij.openapi.util.text.StringUtil
|
|
|
|
|
import com.intellij.psi.JavaPsiFacade
|
|
|
|
|
import com.intellij.psi.PsiElement
|
|
|
|
|
import com.intellij.psi.PsiElementVisitor
|
|
|
|
|
import com.intellij.psi.PsiNameIdentifierOwner
|
|
|
|
|
import com.intellij.ui.EditorTextField
|
|
|
|
|
import com.siyeh.ig.BaseGlobalInspection
|
|
|
|
|
import com.siyeh.ig.psiutils.TestUtils
|
|
|
|
|
import org.intellij.lang.annotations.Language
|
|
|
|
|
import org.intellij.lang.regexp.RegExpFileType
|
|
|
|
|
import org.jdom.Element
|
|
|
|
|
import org.jetbrains.annotations.NonNls
|
|
|
|
|
import org.jetbrains.kotlin.idea.core.packageMatchesDirectoryOrImplicit
|
|
|
|
|
import org.jetbrains.kotlin.idea.quickfix.RenameIdentifierFix
|
|
|
|
|
import org.jetbrains.kotlin.idea.refactoring.isInjectedFragment
|
|
|
|
|
import org.jetbrains.kotlin.idea.util.compat.psiFile
|
|
|
|
|
import org.jetbrains.kotlin.lexer.KtTokens
|
|
|
|
|
import org.jetbrains.kotlin.psi.*
|
|
|
|
|
import org.jetbrains.kotlin.psi.psiUtil.containingClassOrObject
|
|
|
|
@@ -104,7 +113,7 @@ class NamingConventionInspectionSettings(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
protected fun getNameMismatchMessage(name: String): String {
|
|
|
|
|
fun getNameMismatchMessage(name: String): String {
|
|
|
|
|
if (namePattern != defaultNamePattern) {
|
|
|
|
|
return getDefaultErrorMessage()
|
|
|
|
|
}
|
|
|
|
@@ -145,7 +154,7 @@ sealed class NamingConventionInspection(
|
|
|
|
|
@Suppress("MemberVisibilityCanBePrivate")
|
|
|
|
|
var namePattern: String = defaultNamePattern
|
|
|
|
|
|
|
|
|
|
protected val namingSettings = NamingConventionInspectionSettings(
|
|
|
|
|
private val namingSettings = NamingConventionInspectionSettings(
|
|
|
|
|
entityName, defaultNamePattern,
|
|
|
|
|
setNamePatternCallback = { value ->
|
|
|
|
|
namePattern = value
|
|
|
|
@@ -297,37 +306,17 @@ class LocalVariableNameInspection :
|
|
|
|
|
START_LOWER, NO_UNDERSCORES, NO_BAD_CHARACTERS
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
class PackageNameInspection :
|
|
|
|
|
NamingConventionInspection("Package", "[a-z_][a-zA-Z\\d_]*(\\.[a-z_][a-zA-Z\\d_]*)*") {
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
val PART_RULES = arrayOf(NO_BAD_CHARACTERS_OR_UNDERSCORE, NO_START_UPPER)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class PackageNameInspectionLocal(
|
|
|
|
|
val parentInspection: InspectionProfileEntry,
|
|
|
|
|
val namingSettings: NamingConventionInspectionSettings
|
|
|
|
|
) : AbstractKotlinInspection() {
|
|
|
|
|
override fun buildVisitor(holder: ProblemsHolder, isOnTheFly: Boolean, session: LocalInspectionToolSession): PsiElementVisitor {
|
|
|
|
|
return packageDirectiveVisitor { directive ->
|
|
|
|
|
val packageNameExpression = directive.packageNameExpression ?: return@packageDirectiveVisitor
|
|
|
|
|
val qualifiedName = directive.qualifiedName
|
|
|
|
|
if (qualifiedName.isEmpty() || namingSettings.nameRegex?.matches(qualifiedName) != false) {
|
|
|
|
|
return@packageDirectiveVisitor
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val partErrorMessage = if (namePattern == namingSettings.defaultNamePattern) {
|
|
|
|
|
directive.packageNames.asSequence()
|
|
|
|
|
.mapNotNull { simpleName ->
|
|
|
|
|
val referencedName = simpleName.getReferencedName()
|
|
|
|
|
findRuleMessage(referencedName, PackageNameInspection.PART_RULES)
|
|
|
|
|
}
|
|
|
|
|
.firstOrNull()
|
|
|
|
|
} else {
|
|
|
|
|
null
|
|
|
|
|
}
|
|
|
|
|
val checkResult = checkPackageDirective(directive, namingSettings) ?: return@packageDirectiveVisitor
|
|
|
|
|
|
|
|
|
|
val descriptionTemplate = if (partErrorMessage != null) {
|
|
|
|
|
"Package name <code>#ref</code> part $partErrorMessage #loc"
|
|
|
|
|
} else {
|
|
|
|
|
"Package name <code>#ref</code> ${namingSettings.getDefaultErrorMessage()} #loc"
|
|
|
|
|
}
|
|
|
|
|
val descriptionTemplate = checkResult.toProblemTemplateString()
|
|
|
|
|
|
|
|
|
|
holder.registerProblem(
|
|
|
|
|
packageNameExpression,
|
|
|
|
@@ -337,10 +326,126 @@ class PackageNameInspection :
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
companion object {
|
|
|
|
|
data class CheckResult(val errorMessage: String, val isForPart: Boolean)
|
|
|
|
|
|
|
|
|
|
fun CheckResult.toProblemTemplateString(): String {
|
|
|
|
|
return if (isForPart) {
|
|
|
|
|
"Package name <code>#ref</code> part $errorMessage #loc"
|
|
|
|
|
} else {
|
|
|
|
|
"Package name <code>#ref</code> $errorMessage #loc"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun checkPackageDirective(directive: KtPackageDirective, namingSettings: NamingConventionInspectionSettings): CheckResult? {
|
|
|
|
|
return checkQualifiedName(directive.qualifiedName, namingSettings)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
fun checkQualifiedName(qualifiedName: String, namingSettings: NamingConventionInspectionSettings): CheckResult? {
|
|
|
|
|
if (qualifiedName.isEmpty() || namingSettings.nameRegex?.matches(qualifiedName) != false) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val partErrorMessage = if (namingSettings.namePattern == namingSettings.defaultNamePattern) {
|
|
|
|
|
qualifiedName.split('.').asSequence()
|
|
|
|
|
.mapNotNull { part -> findRuleMessage(part, PackageNameInspection.PART_RULES) }
|
|
|
|
|
.firstOrNull()
|
|
|
|
|
} else {
|
|
|
|
|
null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return if (partErrorMessage != null) {
|
|
|
|
|
CheckResult(partErrorMessage, true)
|
|
|
|
|
} else {
|
|
|
|
|
CheckResult(namingSettings.getDefaultErrorMessage(), false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
private class RenamePackageFix : RenameIdentifierFix() {
|
|
|
|
|
override fun getElementToRename(element: PsiElement): PsiElement? {
|
|
|
|
|
val packageDirective = element as? KtPackageDirective ?: return null
|
|
|
|
|
return JavaPsiFacade.getInstance(element.project).findPackage(packageDirective.qualifiedName)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun getShortName(): String = parentInspection.shortName
|
|
|
|
|
override fun getDisplayName(): String = parentInspection.displayName
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class PackageNameInspection : BaseGlobalInspection() {
|
|
|
|
|
companion object {
|
|
|
|
|
const val DEFAULT_PACKAGE_NAME_PATTERN = "[a-z_][a-zA-Z\\d_]*(\\.[a-z_][a-zA-Z\\d_]*)*"
|
|
|
|
|
val PART_RULES = arrayOf(NO_BAD_CHARACTERS_OR_UNDERSCORE, NO_START_UPPER)
|
|
|
|
|
|
|
|
|
|
private fun PackageNameInspectionLocal.Companion.CheckResult.toErrorMessage(qualifiedName: String): String {
|
|
|
|
|
return if (isForPart) {
|
|
|
|
|
"Package name <code>$qualifiedName</code> part $errorMessage"
|
|
|
|
|
} else {
|
|
|
|
|
"Package name <code>$qualifiedName</code> $errorMessage"
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var namePattern: String = DEFAULT_PACKAGE_NAME_PATTERN
|
|
|
|
|
|
|
|
|
|
private val namingSettings = NamingConventionInspectionSettings(
|
|
|
|
|
"Package",
|
|
|
|
|
DEFAULT_PACKAGE_NAME_PATTERN,
|
|
|
|
|
setNamePatternCallback = { value ->
|
|
|
|
|
namePattern = value
|
|
|
|
|
}
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
override fun checkElement(
|
|
|
|
|
refEntity: RefEntity,
|
|
|
|
|
analysisScope: AnalysisScope,
|
|
|
|
|
inspectionManager: InspectionManager,
|
|
|
|
|
globalInspectionContext: GlobalInspectionContext
|
|
|
|
|
): Array<CommonProblemDescriptor>? {
|
|
|
|
|
when (refEntity) {
|
|
|
|
|
is RefFile -> {
|
|
|
|
|
val psiFile = refEntity.psiFile
|
|
|
|
|
if (psiFile is KtFile && !psiFile.isInjectedFragment && !psiFile.packageMatchesDirectoryOrImplicit()) {
|
|
|
|
|
val packageDirective = psiFile.packageDirective
|
|
|
|
|
if (packageDirective != null) {
|
|
|
|
|
val qualifiedName = packageDirective.qualifiedName
|
|
|
|
|
val checkResult = PackageNameInspectionLocal.checkPackageDirective(packageDirective, namingSettings)
|
|
|
|
|
if (checkResult != null) {
|
|
|
|
|
return arrayOf(inspectionManager.createProblemDescriptor(checkResult.toErrorMessage(qualifiedName)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
is RefPackage -> {
|
|
|
|
|
@NonNls val name = StringUtil.getShortName(refEntity.getQualifiedName())
|
|
|
|
|
if (name.isEmpty() || InspectionsBundle.message("inspection.reference.default.package") == name) {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val checkResult = PackageNameInspectionLocal.checkQualifiedName(name, namingSettings)
|
|
|
|
|
if (checkResult != null) {
|
|
|
|
|
return arrayOf(inspectionManager.createProblemDescriptor(checkResult.toErrorMessage(name)))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
else -> {
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun readSettings(element: Element) {
|
|
|
|
|
super.readSettings(element)
|
|
|
|
|
namingSettings.namePattern = namePattern
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
override fun createOptionsPanel() = namingSettings.createOptionsPanel()
|
|
|
|
|
|
|
|
|
|
override fun getSharedLocalInspectionTool(): LocalInspectionTool {
|
|
|
|
|
return PackageNameInspectionLocal(this, namingSettings)
|
|
|
|
|
}
|
|
|
|
|
}
|