diff --git a/java/java-features-trainer/resources/messages/JavaLessonsBundle.properties b/java/java-features-trainer/resources/messages/JavaLessonsBundle.properties index 8421eb79e92a..3ae7022c0bb0 100644 --- a/java/java-features-trainer/resources/messages/JavaLessonsBundle.properties +++ b/java/java-features-trainer/resources/messages/JavaLessonsBundle.properties @@ -1,6 +1,13 @@ #Need to translate in pair with action.EditorToggleCase.text key! toggle.case.part=case +java.missed.sdk.click.setup= cannot find JDK on your computer. The IDE needs JDK to run Java programs and provide code assistance to you. \ + To configure JDK, click the highlighted {0} link. +java.missed.sdk.show.options=Click to see available options +java.missed.sdk.read.more.tip=Tip: you can read more about JDK in the documentation. +java.missed.sdk.configure=Configure JDK by using one of the suggested options. You can download it or specify a path to the existing JDK home directory on your computer. +java.missed.sdk.wait.installation=Please wait while the IDE installs JDK + java.onboarding.module.name=Onboarding tour java.onboarding.module.description=A brief overview of the main features in {0}. diff --git a/java/java-features-trainer/src/com/intellij/java/ift/JavaLangSupport.kt b/java/java-features-trainer/src/com/intellij/java/ift/JavaLangSupport.kt index eaba8bbd3a32..f1beda9ee47a 100644 --- a/java/java-features-trainer/src/com/intellij/java/ift/JavaLangSupport.kt +++ b/java/java-features-trainer/src/com/intellij/java/ift/JavaLangSupport.kt @@ -1,8 +1,28 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.java.ift +import com.intellij.openapi.application.runWriteAction +import com.intellij.openapi.progress.util.AbstractProgressIndicatorExBase +import com.intellij.openapi.project.Project +import com.intellij.openapi.project.ProjectBundle +import com.intellij.openapi.roots.OrderRootType +import com.intellij.openapi.roots.ProjectRootManager +import com.intellij.openapi.roots.ui.configuration.JdkComboBox +import com.intellij.openapi.roots.ui.configuration.SdkListItem +import com.intellij.openapi.roots.ui.configuration.projectRoot.SdkDownloadTracker +import com.intellij.openapi.ui.popup.Balloon +import com.intellij.ui.HyperlinkLabel +import com.intellij.ui.components.panels.NonOpaquePanel +import training.dsl.LearningBalloonConfig +import training.dsl.LessonContext +import training.dsl.LessonUtil +import training.learn.course.KLesson import training.project.ReadMeCreator +import training.ui.LearningUiHighlightingManager +import training.ui.LearningUiHighlightingManager.HighlightingOptions +import training.ui.LearningUiUtil import training.util.getFeedbackLink +import javax.swing.JList internal class JavaLangSupport : JavaBasedLangSupport() { override val contentRootDirectoryName = "IdeaLearningProject" @@ -20,4 +40,75 @@ internal class JavaLangSupport : JavaBasedLangSupport() { get() = getFeedbackLink(this, false) override val readMeCreator by lazy { ReadMeCreator() } + + override val sdkConfigurationTasks: LessonContext.(lesson: KLesson) -> Unit = { + val setupSdkText = ProjectBundle.message("project.sdk.setup") + + fun isJdkInstalled(project: Project) = JavaProjectUtil.getProjectJdk(project) != null + + task { + if (isJdkInstalled(project)) return@task + triggerAndFullHighlight { usePulsation = true }.component { ui: HyperlinkLabel -> + ui.text == setupSdkText + } + } + + task { + if (isJdkInstalled(project)) return@task + + rehighlightPreviousUi = true + text(JavaLessonsBundle.message("java.missed.sdk.click.setup", strong(setupSdkText))) + text(JavaLessonsBundle.message("java.missed.sdk.show.options"), LearningBalloonConfig(Balloon.Position.below, 0)) + text(JavaLessonsBundle.message("java.missed.sdk.read.more.tip", LessonUtil.getHelpLink("sdk.html#jdk"))) + triggerAndBorderHighlight().component { list: JList<*> -> + val model = list.model + (0 until model.size).any { + model.getElementAt(it).let { item -> item is SdkListItem || item is JdkComboBox.JdkComboBoxItem } + } + } + } + + task { + if (isJdkInstalled(project)) return@task + + rehighlightPreviousUi = true + text(JavaLessonsBundle.message("java.missed.sdk.configure")) + var progressHighlightingStarted = false + var downloadingListenerAdded = false + timerCheck { + val jdk = JavaProjectUtil.getProjectJdk(project) + + if (jdk != null && !progressHighlightingStarted) { + progressHighlightingStarted = true + invokeInBackground { + LearningUiUtil.findComponentOrNull(project, NonOpaquePanel::class.java) { panel -> + panel.javaClass.name.contains("InlineProgressPanel") + }?.let { panel -> + taskInvokeLater { + if (!disposed) { + LearningUiHighlightingManager.highlightComponent(panel, HighlightingOptions(highlightInside = false)) + this@task.text(JavaLessonsBundle.message("java.missed.sdk.wait.installation"), + LearningBalloonConfig(Balloon.Position.above, width = 0, highlightingComponent = panel)) + } + } + } + } + } + + if (jdk != null && !downloadingListenerAdded) { + downloadingListenerAdded = SdkDownloadTracker.getInstance().tryRegisterDownloadingListener(jdk, taskDisposable, + AbstractProgressIndicatorExBase()) { + // todo: For some reason after the downloading JDK is not fully applied, imports are still red. + // So we need to trigger the indexing by setting the same JDK that already set just to call listeners of JDK update. + // Remove this hack when IDEA-244649 will be fixed. + runWriteAction { + ProjectRootManager.getInstance(project).projectSdk = JavaProjectUtil.getProjectJdk(project) + } + } + } + + jdk != null && jdk.rootProvider.getFiles(OrderRootType.CLASSES).isNotEmpty() + } + } + } } diff --git a/java/java-features-trainer/src/com/intellij/java/ift/JavaProjectUtil.kt b/java/java-features-trainer/src/com/intellij/java/ift/JavaProjectUtil.kt index 5a3a0025ea88..8889495d7b6d 100644 --- a/java/java-features-trainer/src/com/intellij/java/ift/JavaProjectUtil.kt +++ b/java/java-features-trainer/src/com/intellij/java/ift/JavaProjectUtil.kt @@ -1,9 +1,7 @@ // Copyright 2000-2022 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license. package com.intellij.java.ift -import com.intellij.openapi.application.invokeAndWaitIfNeeded import com.intellij.openapi.module.ModuleManager -import com.intellij.openapi.project.DumbService import com.intellij.openapi.project.Project import com.intellij.openapi.projectRoots.JavaSdk import com.intellij.openapi.projectRoots.Sdk @@ -11,43 +9,24 @@ import com.intellij.openapi.roots.ModuleRootManager import com.intellij.openapi.roots.ProjectRootManager import com.intellij.openapi.roots.ui.configuration.SdkLookup import com.intellij.openapi.roots.ui.configuration.SdkLookupDecision -import com.intellij.openapi.ui.Messages import com.intellij.util.lang.JavaVersion -import training.learn.LearnBundle -import training.project.ProjectUtils object JavaProjectUtil { fun findJavaSdkAsync(project: Project, onSdkSearchCompleted: (Sdk?) -> Unit) { val javaSdkType = JavaSdk.getInstance() - val notification = ProjectUtils.createSdkDownloadingNotification() SdkLookup.newLookupBuilder() + .withProject(project) .withSdkType(javaSdkType) .withVersionFilter { JavaVersion.tryParse(it)?.isAtLeast(6) == true } - .onDownloadableSdkSuggested { sdkFix -> - val userDecision = invokeAndWaitIfNeeded { - Messages.showYesNoDialog( - LearnBundle.message("learn.project.initializing.jdk.download.message", sdkFix.downloadDescription), - LearnBundle.message("learn.project.initializing.jdk.download.title"), - null - ) - } - if (userDecision == Messages.YES) { - notification.notify(project) - SdkLookupDecision.CONTINUE - } - else { - onSdkSearchCompleted(null) - SdkLookupDecision.STOP - } + .onDownloadableSdkSuggested { + onSdkSearchCompleted(null) + SdkLookupDecision.STOP } .onSdkResolved { sdk -> if (sdk != null && sdk.sdkType === javaSdkType) { onSdkSearchCompleted(sdk) - DumbService.getInstance(project).runWhenSmart { - notification.expire() - } } } .executeLookup() diff --git a/java/java-features-trainer/src/com/intellij/java/ift/lesson/essential/JavaOnboardingTourLesson.kt b/java/java-features-trainer/src/com/intellij/java/ift/lesson/essential/JavaOnboardingTourLesson.kt index 2c8af73d7845..14c9a69c79fb 100644 --- a/java/java-features-trainer/src/com/intellij/java/ift/lesson/essential/JavaOnboardingTourLesson.kt +++ b/java/java-features-trainer/src/com/intellij/java/ift/lesson/essential/JavaOnboardingTourLesson.kt @@ -146,10 +146,12 @@ class JavaOnboardingTourLesson : KLesson("java.onboarding", JavaLessonsBundle.me projectTasks() - prepareSample(sample) + prepareSample(sample, checkSdkConfiguration = false) openLearnToolwindow() + sdkConfigurationTasks() + waitIndexingTasks() runTasks() @@ -244,12 +246,13 @@ class JavaOnboardingTourLesson : KLesson("java.onboarding", JavaLessonsBundle.me } val currentJdk = JavaProjectUtil.getProjectJdk(project) - val currentJdkVersion: @NlsSafe String = if (currentJdk != null) { - JavaSdk.getInstance().getVersionString(currentJdk) ?: "none" - } - else "none" + + @Suppress("HardCodedStringLiteral") + val currentJdkVersion: @NlsSafe String = currentJdk?.let { JavaSdk.getInstance().getVersionString(it) } ?: "none" val module = ModuleManager.getInstance(project).modules.first() + + @Suppress("HardCodedStringLiteral") val currentLanguageLevel: @NlsSafe String = LanguageLevelUtil.getEffectiveLanguageLevel(module).name primaryLanguage.onboardingFeedbackData = object : OnboardingFeedbackData("IDEA Onboarding Tour Feedback", lessonEndInfo) { diff --git a/plugins/ide-features-trainer/res/messages/LearnBundle.properties b/plugins/ide-features-trainer/res/messages/LearnBundle.properties index 6d16a8ba9369..eb5b3434c7d8 100755 --- a/plugins/ide-features-trainer/res/messages/LearnBundle.properties +++ b/plugins/ide-features-trainer/res/messages/LearnBundle.properties @@ -13,11 +13,6 @@ dialog.askToSwitchToLearnProject.message=This lesson/module should be open Switch to the {0} project manually and start this lesson again. learn.project.initializing.process=Prepare a Learning Project -learn.project.initializing.jdk.download.title=JDK Not Found -learn.project.initializing.jdk.download.message=Cannot find any installed JDK. Download and install {0}? -learn.project.initializing.jdk.download.notification.title=Project initialization -learn.project.initializing.jdk.download.notification.message={0} is downloading and configuring a project SDK. \ - The lesson will begin after the project initialization is completed. learn.project.initializing.choose.place=Choose a Parent Directory for {0} learn.project.initializing.cannot.create.message=Cannot create a learning demo project. See the LOG files for more details. diff --git a/plugins/ide-features-trainer/src/training/dsl/TaskRuntimeContext.kt b/plugins/ide-features-trainer/src/training/dsl/TaskRuntimeContext.kt index 040525d62535..e88ea9f36721 100644 --- a/plugins/ide-features-trainer/src/training/dsl/TaskRuntimeContext.kt +++ b/plugins/ide-features-trainer/src/training/dsl/TaskRuntimeContext.kt @@ -34,6 +34,7 @@ open class TaskRuntimeContext internal constructor(private val lessonExecutor: L : this(base.lessonExecutor, base.actionsRecorder, base.restorePreviousTaskCallback, base.previousGetter) val taskDisposable: Disposable = actionsRecorder + val disposed: Boolean get() = actionsRecorder.disposed val editor: Editor get() = lessonExecutor.editor val project: Project get() = lessonExecutor.project diff --git a/plugins/ide-features-trainer/src/training/project/ProjectUtils.kt b/plugins/ide-features-trainer/src/training/project/ProjectUtils.kt index 33c3d21679b7..642e8547f592 100644 --- a/plugins/ide-features-trainer/src/training/project/ProjectUtils.kt +++ b/plugins/ide-features-trainer/src/training/project/ProjectUtils.kt @@ -8,8 +8,6 @@ import com.intellij.ide.ReopenProjectAction import com.intellij.ide.impl.OpenProjectTask import com.intellij.ide.impl.TrustedPaths import com.intellij.ide.util.PropertiesComponent -import com.intellij.notification.Notification -import com.intellij.notification.NotificationType import com.intellij.openapi.application.* import com.intellij.openapi.diagnostic.thisLogger import com.intellij.openapi.fileChooser.FileChooserDescriptor @@ -32,7 +30,6 @@ import training.lang.LangManager import training.lang.LangSupport import training.learn.LearnBundle import training.util.featureTrainerVersion -import training.util.iftNotificationGroup import java.io.File import java.io.FileFilter import java.io.IOException @@ -286,13 +283,6 @@ object ProjectUtils { } } - fun createSdkDownloadingNotification(): Notification { - return iftNotificationGroup.createNotification(LearnBundle.message("learn.project.initializing.jdk.download.notification.title"), - LearnBundle.message("learn.project.initializing.jdk.download.notification.message", - ApplicationNamesInfo.getInstance().fullProductName), - NotificationType.INFORMATION) - } - fun closeAllEditorsInProject(project: Project) { FileEditorManagerEx.getInstanceEx(project).windows.forEach { it.files.forEach { file -> it.closeFile(file) }