mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[IFT] Propose to choose python interpreter when start learning from welcome screen and no SDK found
IJ-CR-12594 GitOrigin-RevId: 5b424cc24239fb2603990995f0c5bd010191a453
This commit is contained in:
committed by
intellij-monorepo-bot
parent
8a271c2334
commit
4d0abe8a06
@@ -89,7 +89,7 @@ class JavaLangSupport : AbstractLangSupport() {
|
||||
.executeLookup()
|
||||
}
|
||||
|
||||
override fun getSdkForProject(project: Project): Sdk? {
|
||||
override fun getSdkForProject(project: Project, selectedSdk: Sdk?): Sdk? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ private class OpenLearnPanel : DumbAwareAction() {
|
||||
toolWindow.show()
|
||||
}
|
||||
else {
|
||||
OpenLessonActivities.openLearnProjectFromWelcomeScreen()
|
||||
OpenLessonActivities.openLearnProjectFromWelcomeScreen(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ abstract class AbstractLangSupport : LangSupport {
|
||||
|
||||
open val readMeCreator: ReadMeCreator? = null
|
||||
|
||||
override fun getSdkForProject(project: Project): Sdk? {
|
||||
override fun getSdkForProject(project: Project, selectedSdk: Sdk?): Sdk? {
|
||||
try {
|
||||
// Use no SDK if it's a valid for this language
|
||||
checkSdk(null, project)
|
||||
|
||||
@@ -48,7 +48,7 @@ interface LangSupport {
|
||||
* @throws NoSdkException in the case no valid SDK is available, yet it's required for the given project
|
||||
*/
|
||||
@Throws(NoSdkException::class)
|
||||
fun getSdkForProject(project: Project): Sdk?
|
||||
fun getSdkForProject(project: Project, selectedSdk: Sdk?): Sdk?
|
||||
|
||||
fun applyProjectSdk(sdk: Sdk, project: Project)
|
||||
|
||||
@@ -74,4 +74,8 @@ interface LangSupport {
|
||||
|
||||
@RequiresBackgroundThread
|
||||
fun cleanupBeforeLessons(project: Project) = Unit
|
||||
|
||||
fun startFromWelcomeFrame(startCallback: (Sdk?) -> Unit) {
|
||||
startCallback(null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import com.intellij.openapi.application.ApplicationManager
|
||||
import com.intellij.openapi.application.ApplicationNamesInfo
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import training.lang.LangSupport
|
||||
import training.learn.exceptons.NoSdkException
|
||||
@@ -14,13 +15,16 @@ import training.project.ProjectUtils
|
||||
object NewLearnProjectUtil {
|
||||
private val LOG = logger<NewLearnProjectUtil>()
|
||||
|
||||
fun createLearnProject(projectToClose: Project?, langSupport: LangSupport, postInitCallback: (learnProject: Project) -> Unit) {
|
||||
fun createLearnProject(projectToClose: Project?,
|
||||
langSupport: LangSupport,
|
||||
selectedSdk: Sdk?,
|
||||
postInitCallback: (learnProject: Project) -> Unit) {
|
||||
val unitTestMode = ApplicationManager.getApplication().isUnitTestMode
|
||||
|
||||
ProjectUtils.importOrOpenProject(langSupport, projectToClose) { newProject ->
|
||||
TipDialog.DISABLE_TIPS_FOR_PROJECT.set(newProject, true)
|
||||
try {
|
||||
val sdkForProject = langSupport.getSdkForProject(newProject)
|
||||
val sdkForProject = langSupport.getSdkForProject(newProject, selectedSdk)
|
||||
if (sdkForProject != null) {
|
||||
langSupport.applyProjectSdk(sdkForProject, newProject)
|
||||
}
|
||||
|
||||
@@ -18,6 +18,7 @@ import com.intellij.openapi.progress.runBackgroundableTask
|
||||
import com.intellij.openapi.project.DumbService
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.roots.ProjectRootManager
|
||||
import com.intellij.openapi.startup.StartupManager
|
||||
import com.intellij.openapi.ui.Messages
|
||||
@@ -96,7 +97,7 @@ internal object OpenLessonActivities {
|
||||
if (!isLearningProject(projectWhereToStartLesson, langSupport)) {
|
||||
//1. learnProject == null and current project has different name then initLearnProject and register post startup open lesson
|
||||
LOG.debug("${projectWhereToStartLesson.name}: 1. learnProject is null or disposed")
|
||||
initLearnProject(projectWhereToStartLesson) {
|
||||
initLearnProject(projectWhereToStartLesson, null) {
|
||||
LOG.debug("${projectWhereToStartLesson.name}: 1. ... LearnProject has been started")
|
||||
openLessonWhenLearnProjectStart(lesson, it)
|
||||
LOG.debug("${projectWhereToStartLesson.name}: 1. ... open lesson when learn project has been started")
|
||||
@@ -290,9 +291,9 @@ internal object OpenLessonActivities {
|
||||
TextEditorWithPreview.openPreviewForFile(project, readme)
|
||||
}
|
||||
|
||||
fun openOnboardingFromWelcomeScreen(onboarding: Lesson) {
|
||||
fun openOnboardingFromWelcomeScreen(onboarding: Lesson, selectedSdk: Sdk?) {
|
||||
StatisticBase.logLearnProjectOpenedForTheFirstTime(StatisticBase.LearnProjectOpeningWay.ONBOARDING_PROMOTER)
|
||||
initLearnProject(null) { project ->
|
||||
initLearnProject(null, selectedSdk) { project ->
|
||||
StartupManager.getInstance(project).runAfterOpened {
|
||||
invokeLater {
|
||||
if (onboarding.properties.canStartInDumbMode) {
|
||||
@@ -308,9 +309,9 @@ internal object OpenLessonActivities {
|
||||
}
|
||||
}
|
||||
|
||||
fun openLearnProjectFromWelcomeScreen() {
|
||||
fun openLearnProjectFromWelcomeScreen(selectedSdk: Sdk?) {
|
||||
StatisticBase.logLearnProjectOpenedForTheFirstTime(StatisticBase.LearnProjectOpeningWay.LEARN_IDE)
|
||||
initLearnProject(null) { project ->
|
||||
initLearnProject(null, selectedSdk) { project ->
|
||||
StartupManager.getInstance(project).runAfterOpened {
|
||||
invokeLater {
|
||||
openReadme(project)
|
||||
@@ -457,7 +458,7 @@ internal object OpenLessonActivities {
|
||||
return vf
|
||||
}
|
||||
|
||||
private fun initLearnProject(projectToClose: Project?, postInitCallback: (learnProject: Project) -> Unit) {
|
||||
private fun initLearnProject(projectToClose: Project?, selectedSdk: Sdk?, postInitCallback: (learnProject: Project) -> Unit) {
|
||||
val langSupport = LangManager.getInstance().getLangSupport() ?: throw Exception("Language for learning plugin is not defined")
|
||||
//if projectToClose is open
|
||||
findLearnProjectInOpenedProjects(langSupport)?.let {
|
||||
@@ -469,7 +470,7 @@ internal object OpenLessonActivities {
|
||||
if (!NewLearnProjectUtil.showDialogOpenLearnProject(projectToClose))
|
||||
return //if user abort to open lesson in a new Project
|
||||
try {
|
||||
NewLearnProjectUtil.createLearnProject(projectToClose, langSupport) { learnProject ->
|
||||
NewLearnProjectUtil.createLearnProject(projectToClose, langSupport, selectedSdk) { learnProject ->
|
||||
langSupport.applyToProjectAfterConfigure().invoke(learnProject)
|
||||
LearningUiManager.learnProject = learnProject
|
||||
runInEdt {
|
||||
|
||||
+11
-15
@@ -1,11 +1,7 @@
|
||||
// Copyright 2000-2021 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package training.ui.welcomeScreen
|
||||
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.DataContext
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.wm.InteractiveCourseData
|
||||
import com.intellij.openapi.wm.InteractiveCourseFactory
|
||||
@@ -14,6 +10,7 @@ import com.intellij.openapi.wm.impl.welcomeScreen.learnIde.LearnIdeContentColors
|
||||
import com.intellij.ui.components.labels.LinkLabel
|
||||
import com.intellij.ui.scale.JBUIScale
|
||||
import training.FeaturesTrainerIcons
|
||||
import training.lang.LangManager
|
||||
import training.learn.CourseManager
|
||||
import training.learn.LearnBundle
|
||||
import training.learn.OpenLessonActivities
|
||||
@@ -131,16 +128,15 @@ private class IFTInteractiveCourseData : InteractiveCourseData {
|
||||
}
|
||||
|
||||
private fun openLearningFromWelcomeScreen(module: IftModule?) {
|
||||
val action = ActionManager.getInstance().getAction("ShowLearnPanel")
|
||||
|
||||
val onboardingLesson = findOnboardingLesson(module)
|
||||
if (onboardingLesson != null) {
|
||||
OpenLessonActivities.openOnboardingFromWelcomeScreen(onboardingLesson)
|
||||
}
|
||||
else {
|
||||
CourseManager.instance.unfoldModuleOnInit = module ?: CourseManager.instance.modules.firstOrNull()
|
||||
val anActionEvent = AnActionEvent.createFromAnAction(action, null, ActionPlaces.WELCOME_SCREEN, DataContext.EMPTY_CONTEXT)
|
||||
ActionUtil.performActionDumbAwareWithCallbacks(action, anActionEvent)
|
||||
LangManager.getInstance().getLangSupport()?.startFromWelcomeFrame { selectedSdk: Sdk? ->
|
||||
val onboardingLesson = findOnboardingLesson(module)
|
||||
if (onboardingLesson != null) {
|
||||
OpenLessonActivities.openOnboardingFromWelcomeScreen(onboardingLesson, selectedSdk)
|
||||
}
|
||||
else {
|
||||
CourseManager.instance.unfoldModuleOnInit = module ?: CourseManager.instance.modules.firstOrNull()
|
||||
OpenLessonActivities.openLearnProjectFromWelcomeScreen(selectedSdk)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+18
-8
@@ -2,15 +2,18 @@
|
||||
package training.ui.welcomeScreen
|
||||
|
||||
import com.intellij.openapi.diagnostic.logger
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.util.SystemInfo
|
||||
import com.intellij.openapi.wm.StartPagePromoter
|
||||
import com.intellij.ui.components.panels.NonOpaquePanel
|
||||
import com.intellij.ui.scale.JBUIScale
|
||||
import com.intellij.util.ui.JBUI
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import org.jetbrains.annotations.ApiStatus
|
||||
import org.jetbrains.annotations.NonNls
|
||||
import training.FeaturesTrainerIcons
|
||||
import training.dsl.LessonUtil
|
||||
import training.lang.LangManager
|
||||
import training.learn.CourseManager
|
||||
import training.learn.LearnBundle
|
||||
import training.learn.OpenLessonActivities
|
||||
@@ -25,6 +28,7 @@ import java.awt.event.ActionEvent
|
||||
import javax.swing.*
|
||||
import javax.swing.border.MatteBorder
|
||||
|
||||
@ApiStatus.Internal
|
||||
open class OnboardingLessonPromoter(@NonNls private val lessonId: String) : StartPagePromoter {
|
||||
open fun promoImage(): Icon = FeaturesTrainerIcons.Img.PluginIcon
|
||||
|
||||
@@ -54,14 +58,7 @@ open class OnboardingLessonPromoter(@NonNls private val lessonId: String) : Star
|
||||
jButton.isOpaque = false
|
||||
jButton.action = object : AbstractAction(LearnBundle.message("welcome.promo.start.tour")) {
|
||||
override fun actionPerformed(e: ActionEvent?) {
|
||||
val lesson = CourseManager.instance.lessonsForModules.find { it.id == lessonId }
|
||||
if (lesson == null) {
|
||||
logger<OnboardingLessonPromoter>().error("No lesson with id $lessonId")
|
||||
return
|
||||
}
|
||||
val primaryLanguage = lesson.module.primaryLanguage ?: error("No primary language for promoting lesson ${lesson.name}")
|
||||
resetPrimaryLanguage(primaryLanguage)
|
||||
OpenLessonActivities.openOnboardingFromWelcomeScreen(lesson)
|
||||
startOnboardingLessonWithSdk()
|
||||
}
|
||||
}
|
||||
vPanel.add(rigid(0, 18))
|
||||
@@ -84,6 +81,19 @@ open class OnboardingLessonPromoter(@NonNls private val lessonId: String) : Star
|
||||
return rPanel
|
||||
}
|
||||
|
||||
protected fun startOnboardingLessonWithSdk() {
|
||||
val lesson = CourseManager.instance.lessonsForModules.find { it.id == lessonId }
|
||||
if (lesson == null) {
|
||||
logger<OnboardingLessonPromoter>().error("No lesson with id $lessonId")
|
||||
return
|
||||
}
|
||||
val primaryLanguage = lesson.module.primaryLanguage ?: error("No primary language for promoting lesson ${lesson.name}")
|
||||
resetPrimaryLanguage(primaryLanguage)
|
||||
LangManager.getInstance().getLangSupport()?.startFromWelcomeFrame { selectedSdk: Sdk? ->
|
||||
OpenLessonActivities.openOnboardingFromWelcomeScreen(lesson, selectedSdk)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buttonPixelHunting(button: JButton): JPanel {
|
||||
|
||||
val buttonSizeWithoutInsets = Dimension(button.preferredSize.width - button.insets.left - button.insets.right,
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
choose.python.sdk.to.start.learning.header=Python Interpreter Is Required
|
||||
|
||||
#Need to translate in pair with action.EditorToggleCase.text key!
|
||||
toggle.case.part=case
|
||||
|
||||
|
||||
@@ -3,27 +3,39 @@ package com.jetbrains.python.ift
|
||||
|
||||
import com.intellij.ide.impl.OpenProjectTask
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.module.Module
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectManager
|
||||
import com.intellij.openapi.projectRoots.Sdk
|
||||
import com.intellij.openapi.projectRoots.impl.SdkConfigurationUtil
|
||||
import com.intellij.openapi.roots.ModuleRootManager
|
||||
import com.intellij.openapi.ui.DialogWrapper
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.util.UserDataHolderBase
|
||||
import com.intellij.openapi.util.io.FileUtil
|
||||
import com.intellij.openapi.vfs.LocalFileSystem
|
||||
import com.intellij.openapi.vfs.VfsUtilCore
|
||||
import com.intellij.openapi.vfs.VirtualFile
|
||||
import com.intellij.util.ui.FormBuilder
|
||||
import com.jetbrains.python.PyBundle
|
||||
import com.jetbrains.python.PySdkBundle
|
||||
import com.jetbrains.python.configuration.PyConfigurableInterpreterList
|
||||
import com.jetbrains.python.sdk.PreferredSdkComparator
|
||||
import com.jetbrains.python.sdk.PySdkSettings
|
||||
import com.jetbrains.python.newProject.steps.ProjectSpecificSettingsStep
|
||||
import com.jetbrains.python.sdk.*
|
||||
import com.jetbrains.python.sdk.add.PySdkPathChoosingComboBox
|
||||
import com.jetbrains.python.sdk.add.addBaseInterpretersAsync
|
||||
import com.jetbrains.python.sdk.configuration.PyProjectSdkConfiguration.setReadyToUseSdk
|
||||
import com.jetbrains.python.sdk.configuration.PyProjectVirtualEnvConfiguration
|
||||
import com.jetbrains.python.sdk.findBaseSdks
|
||||
import com.jetbrains.python.sdk.pythonSdk
|
||||
import com.jetbrains.python.statistics.modules
|
||||
import training.lang.AbstractLangSupport
|
||||
import training.project.ProjectUtils
|
||||
import training.project.ReadMeCreator
|
||||
import training.util.getFeedbackLink
|
||||
import java.awt.Dimension
|
||||
import java.nio.file.Path
|
||||
import javax.swing.JComponent
|
||||
import javax.swing.JLabel
|
||||
import kotlin.math.max
|
||||
|
||||
class PythonLangSupport : AbstractLangSupport() {
|
||||
override val defaultProjectName = "PyCharmLearningProject"
|
||||
@@ -68,7 +80,12 @@ class PythonLangSupport : AbstractLangSupport() {
|
||||
ProjectUtils.simpleInstallAndOpenLearningProject(projectPath, this, openProjectTask, postInitCallback)
|
||||
}
|
||||
|
||||
override fun getSdkForProject(project: Project): Sdk? {
|
||||
override fun getSdkForProject(project: Project, selectedSdk: Sdk?): Sdk? {
|
||||
if (selectedSdk != null) {
|
||||
val module = project.modules.first()
|
||||
val existingSdks = getExistingSdks()
|
||||
return applyBaseSdk(project, selectedSdk, existingSdks, module)
|
||||
}
|
||||
if (project.pythonSdk != null) return null // sdk already configured
|
||||
return createVenv(project)
|
||||
}
|
||||
@@ -78,6 +95,13 @@ class PythonLangSupport : AbstractLangSupport() {
|
||||
val existingSdks = getExistingSdks()
|
||||
val baseSdks = findBaseSdks(existingSdks, module, project)
|
||||
val preferredSdk = PyProjectVirtualEnvConfiguration.findPreferredVirtualEnvBaseSdk(baseSdks)
|
||||
return applyBaseSdk(project, preferredSdk, existingSdks, module)
|
||||
}
|
||||
|
||||
private fun applyBaseSdk(project: Project,
|
||||
preferredSdk: Sdk?,
|
||||
existingSdks: List<Sdk>,
|
||||
module: Module?): Sdk? {
|
||||
val venvRoot = FileUtil.toSystemDependentName(PySdkSettings.instance.getPreferredVirtualEnvBasePath(project.basePath))
|
||||
val venvSdk = PyProjectVirtualEnvConfiguration.createVirtualEnvSynchronously(preferredSdk, existingSdks, venvRoot,
|
||||
project.basePath, project, module, project)
|
||||
@@ -103,4 +127,50 @@ class PythonLangSupport : AbstractLangSupport() {
|
||||
}
|
||||
|
||||
override val projectSandboxRelativePath = "src/sandbox.py"
|
||||
|
||||
override fun startFromWelcomeFrame(startCallback: (Sdk?) -> Unit) {
|
||||
val allExistingSdks = listOf(*PyConfigurableInterpreterList.getInstance(null).model.sdks)
|
||||
val existingSdks = ProjectSpecificSettingsStep.getValidPythonSdks(allExistingSdks)
|
||||
val context = UserDataHolderBase()
|
||||
val baseSdks = findBaseSdks(existingSdks, null, context)
|
||||
if (baseSdks.isEmpty()) {
|
||||
val baseSdkField = PySdkPathChoosingComboBox()
|
||||
|
||||
val warningPlaceholder = JLabel()
|
||||
val formPanel = FormBuilder.createFormBuilder()
|
||||
.addComponent(warningPlaceholder)
|
||||
.addLabeledComponent(PySdkBundle.message("python.venv.base.label"), baseSdkField)
|
||||
.panel
|
||||
|
||||
formPanel.preferredSize = Dimension(max(formPanel.preferredSize.width, 500), formPanel.preferredSize.height)
|
||||
val dialog = object : DialogWrapper(ProjectManager.getInstance().defaultProject) {
|
||||
override fun createCenterPanel(): JComponent = formPanel
|
||||
|
||||
init {
|
||||
title = PyBundle.message("sdk.select.path")
|
||||
init()
|
||||
}
|
||||
}
|
||||
|
||||
addBaseInterpretersAsync(baseSdkField, existingSdks, null, context) {
|
||||
val selectedSdk = baseSdkField.selectedSdk
|
||||
if (selectedSdk is PySdkToInstall) {
|
||||
val installationWarning = selectedSdk.getInstallationWarning(Messages.getOkButton())
|
||||
warningPlaceholder.text = "<html>$installationWarning</html>"
|
||||
}
|
||||
else {
|
||||
warningPlaceholder.text = ""
|
||||
}
|
||||
}
|
||||
|
||||
dialog.title = PythonLessonsBundle.message("choose.python.sdk.to.start.learning.header")
|
||||
if (dialog.showAndGet()) {
|
||||
val selectedSdk = baseSdkField.selectedSdk
|
||||
if (selectedSdk == null) return
|
||||
startCallback(selectedSdk)
|
||||
}
|
||||
} else {
|
||||
startCallback(null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -359,7 +359,7 @@ public class ProjectSpecificSettingsStep<T> extends ProjectSettingsStepBase<T> i
|
||||
}
|
||||
|
||||
@NotNull
|
||||
private static List<Sdk> getValidPythonSdks(@NotNull List<Sdk> existingSdks) {
|
||||
public static List<Sdk> getValidPythonSdks(@NotNull List<Sdk> existingSdks) {
|
||||
return StreamEx
|
||||
.of(existingSdks)
|
||||
.filter(sdk -> sdk != null && sdk.getSdkType() instanceof PythonSdkType && !PythonSdkUtil.isInvalid(sdk))
|
||||
|
||||
@@ -93,7 +93,7 @@ private fun getPy38ToInstallOnWindows(): PySdkToInstallOnWindows {
|
||||
)
|
||||
}
|
||||
|
||||
internal abstract class PySdkToInstall internal constructor(name: String, version: String)
|
||||
abstract class PySdkToInstall internal constructor(name: String, version: String)
|
||||
: ProjectJdkImpl(name, PythonSdkType.getInstance(), null, version) {
|
||||
|
||||
@CalledInAny
|
||||
|
||||
Reference in New Issue
Block a user