diff --git a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
index c4238f3163f9..cb6e21fa078b 100644
--- a/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
+++ b/plugins/devkit/devkit-core/resources/intellij.devkit.core.xml
@@ -580,6 +580,9 @@
+
@@ -654,6 +657,11 @@
+
+
+
diff --git a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
index f50e82a61048..76af8ad6883a 100644
--- a/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
+++ b/plugins/devkit/devkit-core/resources/messages/DevKitBundle.properties
@@ -646,6 +646,9 @@ inspection.light.service.migration.app.level.message=Service can be converted to
Annotate the service class with '@Service' and remove its registration from the 'plugin.xml' file.
inspection.light.service.migration.project.level.message=Service can be converted to a light one.\n\
Annotate the service class with '@Service(Service.Level.PROJECT)' and remove its registration from the 'plugin.xml' file.
+inspection.light.service.migration.family.name=Convert to light service
+inspection.light.service.migration.app.level.fix=Annotate the service class with '@Service' and remove its registration from the 'plugin.xml' file
+inspection.light.service.migration.project.level.fix=Annotate the service class with '@Service(Service.Level.PROJECT)' and remove its registration from the 'plugin.xml' file
inspection.light.service.migration.already.annotated.message=Service class annotated with '@Service' must not be registered in the 'plugin.xml' file
inspection.action.presentation.instantiated.in.ctor.display.name=Eager creation of action presentation
diff --git a/plugins/devkit/devkit-core/src/inspections/quickfix/ConvertToLightServiceFix.kt b/plugins/devkit/devkit-core/src/inspections/quickfix/ConvertToLightServiceFix.kt
new file mode 100644
index 000000000000..b2cb35422f91
--- /dev/null
+++ b/plugins/devkit/devkit-core/src/inspections/quickfix/ConvertToLightServiceFix.kt
@@ -0,0 +1,81 @@
+// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.idea.devkit.inspections.quickfix
+
+import com.intellij.codeInsight.ExternalAnnotationsManager
+import com.intellij.codeInsight.intention.AddAnnotationFix
+import com.intellij.codeInsight.intention.AddAnnotationPsiFix
+import com.intellij.codeInsight.intention.preview.IntentionPreviewInfo
+import com.intellij.codeInspection.LocalQuickFix
+import com.intellij.codeInspection.ProblemDescriptor
+import com.intellij.lang.LanguageExtension
+import com.intellij.openapi.components.Service
+import com.intellij.openapi.extensions.ExtensionPointName
+import com.intellij.openapi.project.Project
+import com.intellij.psi.JavaPsiFacade
+import com.intellij.psi.PsiClass
+import com.intellij.psi.PsiElement
+import com.intellij.psi.SmartPsiElementPointer
+import com.intellij.psi.xml.XmlTag
+import com.intellij.refactoring.suggested.createSmartPointer
+import com.intellij.util.xml.highlighting.RemoveDomElementQuickFix
+import org.jetbrains.idea.devkit.DevKitBundle
+import org.jetbrains.idea.devkit.inspections.getProjectLevelFQN
+
+internal class ConvertToLightServiceFix private constructor(private val classPointer: SmartPsiElementPointer,
+ private val xmlTagPointer: SmartPsiElementPointer,
+ private val level: Service.Level) : LocalQuickFix {
+
+ constructor(aClass: PsiElement, xmlTag: XmlTag, level: Service.Level) : this(aClass.createSmartPointer(),
+ xmlTag.createSmartPointer(),
+ level)
+
+ override fun getFamilyName(): String {
+ return DevKitBundle.message("inspection.light.service.migration.family.name")
+ }
+
+ override fun getName(): String {
+ val key = when (level) {
+ Service.Level.APP -> "inspection.light.service.migration.app.level.fix"
+ Service.Level.PROJECT -> "inspection.light.service.migration.project.level.fix"
+ }
+ return DevKitBundle.message(key)
+ }
+
+ override fun generatePreview(project: Project, previewDescriptor: ProblemDescriptor): IntentionPreviewInfo {
+ return IntentionPreviewInfo.EMPTY
+ }
+
+ override fun applyFix(project: Project, descriptor: ProblemDescriptor) {
+ val aClass = classPointer.element ?: return
+ val xmlTag = xmlTagPointer.element ?: return
+ val addServiceAnnotationProvider = AddServiceAnnotationProviders.forLanguage(aClass.language) ?: return
+ addServiceAnnotationProvider.addServiceAnnotation(aClass, level)
+ RemoveDomElementQuickFix.removeXmlTag(xmlTag, project)
+ }
+}
+
+private val EP_NAME: ExtensionPointName =
+ ExtensionPointName.create("DevKit.lang.addServiceAnnotationProvider")
+
+internal object AddServiceAnnotationProviders : LanguageExtension(EP_NAME.name)
+
+interface AddServiceAnnotationProvider {
+ fun addServiceAnnotation(aClass: PsiElement, level: Service.Level)
+}
+
+internal class JavaAddServiceAnnotationProvider : AddServiceAnnotationProvider {
+ override fun addServiceAnnotation(aClass: PsiElement, level: Service.Level) {
+ if (aClass !is PsiClass) return
+ val fix = when (level) {
+ Service.Level.APP -> AddAnnotationPsiFix(Service::class.java.canonicalName, aClass)
+ Service.Level.PROJECT -> {
+ val factory = JavaPsiFacade.getElementFactory(aClass.project)
+ val projectLevelFqn = getProjectLevelFQN()
+ val newAnnotation = factory.createAnnotationFromText("@${Service::class.java.canonicalName}(${projectLevelFqn})", aClass)
+ val attributes = newAnnotation.parameterList.attributes
+ AddAnnotationFix(Service::class.java.canonicalName, aClass, attributes, ExternalAnnotationsManager.AnnotationPlace.IN_CODE)
+ }
+ }
+ fix.invoke(aClass.project, aClass.containingFile, aClass, aClass)
+ }
+}
\ No newline at end of file
diff --git a/plugins/devkit/intellij.kotlin.devkit/resources/intellij.kotlin.devkit.xml b/plugins/devkit/intellij.kotlin.devkit/resources/intellij.kotlin.devkit.xml
index e2ee2ad37174..4cb2aa2d0e66 100644
--- a/plugins/devkit/intellij.kotlin.devkit/resources/intellij.kotlin.devkit.xml
+++ b/plugins/devkit/intellij.kotlin.devkit/resources/intellij.kotlin.devkit.xml
@@ -82,5 +82,8 @@
+
diff --git a/plugins/devkit/intellij.kotlin.devkit/src/inspections/KotlinAddServiceAnnotationProvider.kt b/plugins/devkit/intellij.kotlin.devkit/src/inspections/KotlinAddServiceAnnotationProvider.kt
new file mode 100644
index 000000000000..57009d06d9b0
--- /dev/null
+++ b/plugins/devkit/intellij.kotlin.devkit/src/inspections/KotlinAddServiceAnnotationProvider.kt
@@ -0,0 +1,39 @@
+// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
+package org.jetbrains.idea.devkit.kotlin.inspections
+
+import com.intellij.openapi.components.Service
+import com.intellij.psi.PsiElement
+import org.jetbrains.idea.devkit.inspections.getProjectLevelFQN
+import org.jetbrains.idea.devkit.inspections.quickfix.AddServiceAnnotationProvider
+import org.jetbrains.kotlin.asJava.classes.KtLightClass
+import org.jetbrains.kotlin.idea.quickfix.AddAnnotationFix
+import org.jetbrains.kotlin.idea.quickfix.AddAnnotationWithArgumentsFix
+import org.jetbrains.kotlin.name.ClassId
+import org.jetbrains.kotlin.name.FqName
+import org.jetbrains.kotlin.psi.KtClass
+
+internal class KotlinAddServiceAnnotationProvider : AddServiceAnnotationProvider {
+ override fun addServiceAnnotation(aClass: PsiElement, level: Service.Level) {
+ val ktClass = when (aClass) {
+ is KtClass -> aClass
+ is KtLightClass -> aClass.kotlinOrigin
+ else -> return
+ }
+ val file = ktClass?.containingFile ?: return
+ val annotationFqName = FqName(Service::class.java.canonicalName)
+ val fix = when (level) {
+ Service.Level.APP -> {
+ val annotationClassId = ClassId.topLevel(annotationFqName)
+ AddAnnotationFix(ktClass, annotationClassId, AddAnnotationFix.Kind.Self)
+ }
+
+ Service.Level.PROJECT -> {
+ val kind = AddAnnotationWithArgumentsFix.Kind.Self
+ val projectLevelFqn = getProjectLevelFQN()
+ val arguments = listOf(projectLevelFqn)
+ AddAnnotationWithArgumentsFix(ktClass, annotationFqName, arguments, kind)
+ }
+ }
+ fix.invoke(file.project, null, file)
+ }
+}
\ No newline at end of file