IDEA-300739 Do not create instances of LangSupport during LangManager initialization

GitOrigin-RevId: a985502bde6f18d6fae7ba33463f7c6fad0b3a09
This commit is contained in:
Konstantin Hudyakov
2022-09-02 15:13:13 +03:00
committed by intellij-monorepo-bot
parent 51ce98424c
commit 68b7c53db2
11 changed files with 102 additions and 33 deletions

View File

@@ -1,6 +1,6 @@
<idea-plugin>
<extensions defaultExtensionNs="training">
<ift.language.extension language="JAVA" implementationClass="com.intellij.java.ift.JavaLangSupport"/>
<ift.language.extension language="JAVA" defaultProductName="IDEA" implementationClass="com.intellij.java.ift.JavaLangSupport"/>
<ift.learning.course language="JAVA" implementationClass="com.intellij.java.ift.JavaLearningCourse"/>
<ifs.suggesterSupport language="JAVA" implementationClass="com.intellij.java.ifs.JavaSuggesterSupport"/>
</extensions>

View File

@@ -31,7 +31,7 @@
<extensionPoints>
<extensionPoint name="ift.language.extension"
beanClass="com.intellij.lang.LanguageExtensionPoint" dynamic="true">
beanClass="training.lang.LangSupportBean" dynamic="true">
<with attribute="implementationClass" implements="training.lang.LangSupport"/>
</extensionPoint>

View File

@@ -2,7 +2,6 @@
package training.actions
import com.intellij.lang.Language
import com.intellij.lang.LanguageExtensionPoint
import com.intellij.openapi.actionSystem.AnAction
import com.intellij.openapi.actionSystem.AnActionEvent
import com.intellij.openapi.actionSystem.DefaultActionGroup
@@ -14,6 +13,7 @@ import com.intellij.util.ui.JBUI
import com.intellij.util.ui.UIUtil
import training.lang.LangManager
import training.lang.LangSupport
import training.lang.LangSupportBean
import training.learn.LearnBundle
import training.ui.LearnToolWindow
import training.util.resetPrimaryLanguage
@@ -23,8 +23,8 @@ internal class ChooseProgrammingLanguageForLearningAction(private val learnToolW
override fun createPopupActionGroup(button: JComponent?): DefaultActionGroup {
val allActionsGroup = DefaultActionGroup()
val supportedLanguagesExtensions = LangManager.getInstance().supportedLanguagesExtensions.sortedBy { it.language }
for (langSupportExt: LanguageExtensionPoint<LangSupport> in supportedLanguagesExtensions) {
val languageId = langSupportExt.language
for (langSupportExt: LangSupportBean in supportedLanguagesExtensions) {
val languageId = langSupportExt.getLang()
val displayName = Language.findLanguageByID(languageId)?.displayName ?: continue
allActionsGroup.add(SelectLanguageAction(languageId, displayName))
}
@@ -50,7 +50,7 @@ internal class ChooseProgrammingLanguageForLearningAction(private val learnToolW
private inner class SelectLanguageAction(private val languageId: String, @NlsSafe displayName: String) : AnAction(displayName) {
override fun actionPerformed(e: AnActionEvent) {
val ep = LangManager.getInstance().supportedLanguagesExtensions.singleOrNull { it.language == languageId } ?: return
resetPrimaryLanguage(ep.instance)
resetPrimaryLanguage(ep.getLang())
learnToolWindow.setModulesPanel()
}
}

View File

@@ -2,7 +2,6 @@
package training.lang
import com.intellij.lang.Language
import com.intellij.lang.LanguageExtensionPoint
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.application.ApplicationNamesInfo
import com.intellij.openapi.components.*
@@ -16,30 +15,33 @@ import training.util.*
Storage(value = trainerPluginConfigName, deprecated = true)
])
class LangManager : SimplePersistentStateComponent<LangManager.State>(State()) {
val supportedLanguagesExtensions: List<LanguageExtensionPoint<LangSupport>>
val supportedLanguagesExtensions: List<LangSupportBean>
get() {
return ExtensionPointName<LanguageExtensionPoint<LangSupport>>(LangSupport.EP_NAME).extensionList
.filter { courseCanBeUsed(it.language) }
return ExtensionPointName<LangSupportBean>(LangSupport.EP_NAME).extensionList
.filter { courseCanBeUsed(it.getLang()) }
}
val languages: List<LanguageExtensionPoint<LangSupport>>
val languages: List<LangSupportBean>
get() = supportedLanguagesExtensions.filter { Language.findLanguageByID(it.language) != null }
private var langSupportRef: LangSupport? by WeakReferenceDelegator()
private val langSupportDelegator = LazyWeakReferenceDelegator {
supportedLanguagesExtensions.find { langBean -> langBean.language == state.languageName }?.instance
}
private val langSupportRef: LangSupport? by langSupportDelegator
init {
val productName = ApplicationNamesInfo.getInstance().productName
val langSupportBeans = languages
val onlyLang =
languages.singleOrNull()
?: languages.singleOrNull { it.instance.defaultProductName == productName }
?: languages.firstOrNull()?.also {
langSupportBeans.singleOrNull()
?: langSupportBeans.singleOrNull { it.defaultProductName == productName }
?: langSupportBeans.firstOrNull()?.also {
if (!ApplicationManager.getApplication().isUnitTestMode) {
logger<LangManager>().warn("No default language for $productName. Selected ${it.language}.")
}
}
if (onlyLang != null) {
langSupportRef = onlyLang.instance
state.languageName = onlyLang.language
}
}
@@ -62,18 +64,26 @@ class LangManager : SimplePersistentStateComponent<LangManager.State>(State()) {
}
// do not call this if LearnToolWindow with modules or learn views due to reinitViews
fun updateLangSupport(langSupport: LangSupport) {
this.langSupportRef = langSupport
state.languageName = supportedLanguagesExtensions.find { it.instance == langSupport }?.language
?: throw Exception("Unable to get language.")
fun updateLangSupport(languageId: String) {
val oldLanguage = state.languageName
state.languageName = supportedLanguagesExtensions.find { it.language == languageId }?.language
?: throw Exception("Unable to find LangSupport for language: $languageId")
if (state.languageName != oldLanguage) {
langSupportDelegator.reset()
}
getAllLearnToolWindows().forEach { it.reinitViews() }
}
fun getLangSupport(): LangSupport? = langSupportRef
fun getLanguageId(): String? = state.languageName
override fun loadState(state: State) {
val oldLanguage = this.state.languageName
super.loadState(state)
langSupportRef = supportedLanguagesExtensions.find { langExt -> langExt.language == state.languageName }?.instance ?: return
if (oldLanguage != state.languageName) {
langSupportDelegator.reset()
}
}
fun getLanguageDisplayName(): String {

View File

@@ -0,0 +1,36 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package training.lang
import com.intellij.lang.Language
import com.intellij.openapi.extensions.CustomLoadingExtensionPointBean
import com.intellij.util.xmlb.annotations.Attribute
import org.jetbrains.annotations.TestOnly
class LangSupportBean : CustomLoadingExtensionPointBean<LangSupport> {
/**
* [Language.getID]
*/
@Attribute("language")
var language: String? = null
/**
* [com.intellij.openapi.application.ApplicationNamesInfo.getProductName]
*/
@Attribute("defaultProductName")
var defaultProductName: String? = null
@Attribute("implementationClass")
var implementationClass: String? = null
constructor() : super()
@TestOnly
constructor(language: String, instance: LangSupport) : super(instance) {
this.language = language
}
fun getLang(): String = language ?: error("Language must be specified for bean: $implementationClass")
override fun getImplementationClassName() = implementationClass
}

View File

@@ -25,9 +25,9 @@ private class FeaturesTrainerSettingsPanel : BoundConfigurable(LearnBundle.messa
.map { LanguageOption(it) }
comboBox(options)
.bindItem({
val languageName = LangManager.getInstance().state.languageName
options.find { it.id == languageName } ?: options[0]
}, { language -> resetPrimaryLanguage(languagesExtensions.first { it.language == language?.id }.instance) })
val languageId = LangManager.getInstance().getLanguageId()
options.find { it.id == languageId } ?: options[0]
}, { language -> language?.let { resetPrimaryLanguage(it.id) } })
}
}
row {

View File

@@ -64,7 +64,8 @@ open class OnboardingLessonPromoter(@NonNls private val lessonId: String,
logger<OnboardingLessonPromoter>().error("No lesson with id $lessonId")
return
}
val primaryLanguage = lesson.module.primaryLanguage ?: error("No primary language for promoting lesson ${lesson.name}")
val primaryLanguage: String = lesson.module.primaryLanguage?.primaryLanguage
?: error("No primary language for promoting lesson ${lesson.name}")
resetPrimaryLanguage(primaryLanguage)
LangManager.getInstance().getLangSupport()?.startFromWelcomeFrame { selectedSdk: Sdk? ->
OpenLessonActivities.openOnboardingFromWelcomeScreen(lesson, selectedSdk)

View File

@@ -0,0 +1,21 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package training.util
import java.lang.ref.WeakReference
import kotlin.properties.ReadOnlyProperty
import kotlin.reflect.KProperty
class LazyWeakReferenceDelegator<T>(private val objGetter: () -> T?) : ReadOnlyProperty<Any?, T?> {
private var reference: WeakReference<T>? = null
override fun getValue(thisRef: Any?, property: KProperty<*>): T? {
if (reference == null) {
reference = WeakReference(objGetter())
}
return reference?.get()
}
fun reset() {
reference = null
}
}

View File

@@ -104,11 +104,11 @@ internal fun clearTrainingProgress() {
LearningUiManager.activeToolWindow = null
}
internal fun resetPrimaryLanguage(activeLangSupport: LangSupport): Boolean {
val old = LangManager.getInstance().getLangSupport()
if (activeLangSupport != old) {
internal fun resetPrimaryLanguage(newLanguageId: String): Boolean {
val oldLanguageId = LangManager.getInstance().getLanguageId()
if (newLanguageId != oldLanguageId) {
LessonManager.instance.stopLesson()
LangManager.getInstance().updateLangSupport(activeLangSupport)
LangManager.getInstance().updateLangSupport(newLanguageId)
LearningUiManager.activeToolWindow?.setModulesPanel()
return true
}

View File

@@ -1,7 +1,6 @@
// Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
package training.simple
import com.intellij.lang.LanguageExtensionPoint
import com.intellij.openapi.application.ApplicationManager
import com.intellij.openapi.extensions.DefaultPluginDescriptor
import com.intellij.openapi.extensions.ExtensionPointName
@@ -10,6 +9,7 @@ import com.intellij.testFramework.registerExtension
import org.junit.Assert
import org.junit.Test
import training.lang.LangSupport
import training.lang.LangSupportBean
import training.learn.course.LearningCourse
abstract class LessonsAndTipsIntegrationTest : BasePlatformTestCase() {
@@ -22,9 +22,9 @@ abstract class LessonsAndTipsIntegrationTest : BasePlatformTestCase() {
val langId = languageId
val langSupport = languageSupport
val EP_NAME = ExtensionPointName<LanguageExtensionPoint<LangSupport>>(LangSupport.EP_NAME)
val EP_NAME = ExtensionPointName<LangSupportBean>(LangSupport.EP_NAME)
if (langId != null && langSupport != null && EP_NAME.extensionList.find { it.language == langId } == null) {
val langExtension = LanguageExtensionPoint(langId, langSupport)
val langExtension = LangSupportBean(langId, langSupport)
// specify fake descriptor because it is required to be not null, but will not be used, because extension instance already created
langExtension.pluginDescriptor = DefaultPluginDescriptor("")
ApplicationManager.getApplication().registerExtension(EP_NAME, langExtension, testRootDisposable)

View File

@@ -1,7 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<idea-plugin>
<extensions defaultExtensionNs="training">
<ift.language.extension language="Python" implementationClass="com.jetbrains.python.ift.PythonLangSupport"/>
<ift.language.extension language="Python" defaultProductName="PyCharm"
implementationClass="com.jetbrains.python.ift.PythonLangSupport"/>
<ift.learning.course language="Python" implementationClass="com.jetbrains.python.ift.PythonLearningCourse"/>
<ifs.suggesterSupport language="Python" implementationClass="com.jetbrains.python.ifs.PythonSuggesterSupport"/>
</extensions>