mirror of
https://gitflic.ru/project/openide/openide.git
synced 2026-09-27 10:03:11 +07:00
[kotlin] Translated onboarding tour to Kotlin, added a few more lessons
^KTIJ-26048 fixed Merge-request: IJ-MR-120001 Merged-by: Frederik Haselmeier <Frederik.Haselmeier@jetbrains.com> GitOrigin-RevId: 355d7d61360183b72a190c1d092e629b9774e0d9
This commit is contained in:
committed by
intellij-monorepo-bot
parent
9464787a8a
commit
30a5cc3323
+9
-43
@@ -1,14 +1,10 @@
|
||||
// Copyright 2000-2019 JetBrains s.r.o. Use of this source code is governed by the Apache 2.0 license that can be found in the LICENSE file.
|
||||
package com.intellij.java.ift.lesson.completion
|
||||
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.course.KLesson
|
||||
|
||||
class JavaSmartTypeCompletionLesson : KLesson("Smart type completion", LessonsBundle.message("smart.completion.lesson.name")) {
|
||||
val sample: LessonSample = parseLessonSample("""
|
||||
class JavaSmartTypeCompletionLesson : SmartTypeCompletionLessonBase() {
|
||||
override val sample: LessonSample = parseLessonSample("""
|
||||
import java.lang.String;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
@@ -34,43 +30,13 @@ class JavaSmartTypeCompletionLesson : KLesson("Smart type completion", LessonsBu
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
override val lessonContent: LessonContext.() -> Unit = {
|
||||
prepareSample(sample)
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.smart.type.completion.apply", action("SmartTypeCompletion"), action("EditorChooseLookupItem")))
|
||||
trigger("SmartTypeCompletion")
|
||||
stateCheck {
|
||||
val text = editor.document.text
|
||||
text.contains("strings = arrayBlockingQueue;") || text.contains("strings = linkedList;")
|
||||
}
|
||||
restoreIfModifiedOrMoved(sample)
|
||||
testSmartCompletion()
|
||||
}
|
||||
override val firstCompletionItem: String = "linkedList"
|
||||
override val firstCompletionCheck: String = "strings = linkedList;"
|
||||
|
||||
override val secondCompletionItem: String = "arrayBlockingQueue"
|
||||
override val secondCompletionCheck: String = "return arrayBlockingQueue.toArray(new String[0]);"
|
||||
|
||||
override fun LessonContext.setCaretForSecondItem() {
|
||||
caret(20, 16)
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.smart.type.completion.return", action("SmartTypeCompletion"), action("EditorChooseLookupItem")))
|
||||
trigger("SmartTypeCompletion")
|
||||
stateCheck {
|
||||
val text = editor.document.text
|
||||
text.contains("return arrayBlockingQueue.toArray(new String[0]);")
|
||||
|| text.contains("return strings.toArray(new String[0]);")
|
||||
}
|
||||
restoreIfModifiedOrMoved()
|
||||
testSmartCompletion()
|
||||
}
|
||||
}
|
||||
|
||||
private fun TaskContext.testSmartCompletion() {
|
||||
test {
|
||||
invokeActionViaShortcut("CTRL SHIFT SPACE")
|
||||
ideFrame {
|
||||
jListContains("arrayBlockingQueue").item(0).doubleClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val helpLinks: Map<String, String> get() = mapOf(
|
||||
Pair(LessonsBundle.message("help.code.completion"),
|
||||
LessonUtil.getHelpLink("auto-completing-code.html")),
|
||||
)
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.ift.lesson.completion
|
||||
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.LessonUtil
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.dsl.TaskContext
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.course.KLesson
|
||||
import training.util.isToStringContains
|
||||
|
||||
abstract class SmartTypeCompletionLessonBase : KLesson("Smart type completion", LessonsBundle.message("smart.completion.lesson.name")) {
|
||||
abstract val sample: LessonSample
|
||||
|
||||
abstract val firstCompletionItem: String
|
||||
abstract val firstCompletionCheck: String
|
||||
|
||||
abstract val secondCompletionItem: String
|
||||
abstract val secondCompletionCheck: String
|
||||
|
||||
abstract fun LessonContext.setCaretForSecondItem()
|
||||
|
||||
override val lessonContent: LessonContext.() -> Unit = {
|
||||
prepareSample(sample)
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.smart.type.completion.apply", action("SmartTypeCompletion"), action("EditorChooseLookupItem")))
|
||||
trigger("SmartTypeCompletion")
|
||||
triggerAndBorderHighlight().listItem {
|
||||
it.isToStringContains(firstCompletionItem)
|
||||
}
|
||||
stateCheck {
|
||||
val text = editor.document.text
|
||||
text.contains(firstCompletionCheck)
|
||||
}
|
||||
restoreIfModifiedOrMoved(sample)
|
||||
testSmartCompletion(firstCompletionItem)
|
||||
}
|
||||
setCaretForSecondItem()
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.smart.type.completion.return", action("SmartTypeCompletion"), action("EditorChooseLookupItem")))
|
||||
trigger("SmartTypeCompletion")
|
||||
triggerAndBorderHighlight().listItem {
|
||||
it.isToStringContains(secondCompletionItem)
|
||||
}
|
||||
stateCheck {
|
||||
val text = editor.document.text
|
||||
text.contains(secondCompletionCheck)
|
||||
}
|
||||
restoreIfModifiedOrMoved()
|
||||
testSmartCompletion(secondCompletionItem)
|
||||
}
|
||||
}
|
||||
|
||||
private fun TaskContext.testSmartCompletion(item: String) {
|
||||
test {
|
||||
invokeActionViaShortcut("CTRL SHIFT SPACE")
|
||||
ideFrame {
|
||||
jListContains(item).item(0).doubleClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override val helpLinks: Map<String, String> get() = mapOf(
|
||||
Pair(LessonsBundle.message("help.code.completion"),
|
||||
LessonUtil.getHelpLink("auto-completing-code.html")),
|
||||
)
|
||||
}
|
||||
+6
-593
@@ -2,113 +2,22 @@
|
||||
package com.intellij.java.ift.lesson.essential
|
||||
|
||||
import com.intellij.codeInsight.daemon.impl.analysis.HighlightingFeature
|
||||
import com.intellij.execution.RunManager
|
||||
import com.intellij.execution.ui.UIExperiment
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereManagerImpl
|
||||
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereUI
|
||||
import com.intellij.ide.ui.UISettings
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.ide.util.gotoByName.GotoActionModel
|
||||
import com.intellij.idea.ActionsBundle
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import com.intellij.java.ift.JavaProjectUtil
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton
|
||||
import com.intellij.openapi.application.invokeLater
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.actions.ToggleCaseAction
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.module.LanguageLevelUtil
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.progress.runBackgroundableTask
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectBundle
|
||||
import com.intellij.openapi.projectRoots.JavaSdk
|
||||
import com.intellij.openapi.projectRoots.SdkType
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkDetector
|
||||
import com.intellij.openapi.ui.MessageDialogBuilder
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.ex.MultiLineLabel
|
||||
import com.intellij.openapi.ui.popup.Balloon
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.util.WindowStateService
|
||||
import com.intellij.openapi.wm.ToolWindowManager
|
||||
import com.intellij.openapi.wm.impl.FocusManagerImpl
|
||||
import com.intellij.ui.IdeUICustomization
|
||||
import com.intellij.ui.UIBundle
|
||||
import com.intellij.ui.components.fields.ExtendableTextField
|
||||
import com.intellij.ui.components.panels.NonOpaquePanel
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.ui.tree.TreeVisitor
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.tree.TreeUtil
|
||||
import com.intellij.xdebugger.XDebuggerManager
|
||||
import com.siyeh.InspectionGadgetsBundle
|
||||
import com.siyeh.IntentionPowerPackBundle
|
||||
import kotlinx.serialization.json.JsonObjectBuilder
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.put
|
||||
import org.jetbrains.annotations.Nls
|
||||
import training.FeaturesTrainerIcons
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.adjustSearchEverywherePosition
|
||||
import training.dsl.LessonUtil.checkEditorModification
|
||||
import training.dsl.LessonUtil.restoreIfModified
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.dsl.LessonUtil.restorePopupPosition
|
||||
import training.learn.LearnBundle
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.course.KLesson
|
||||
import training.learn.course.LessonProperties
|
||||
import training.learn.lesson.LessonManager
|
||||
import training.learn.lesson.general.run.clearBreakpoints
|
||||
import training.learn.lesson.general.run.toggleBreakpointTask
|
||||
import training.project.ProjectUtils
|
||||
import training.ui.LearningUiHighlightingManager
|
||||
import training.ui.LearningUiManager
|
||||
import training.ui.getFeedbackProposedPropertyName
|
||||
import training.util.*
|
||||
import java.awt.Point
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.swing.JTree
|
||||
import javax.swing.JWindow
|
||||
import javax.swing.tree.TreePath
|
||||
|
||||
class JavaOnboardingTourLesson : KLesson("java.onboarding", JavaLessonsBundle.message("java.onboarding.lesson.name")) {
|
||||
private lateinit var openLearnTaskId: TaskContext.TaskId
|
||||
private var useDelay: Boolean = false
|
||||
|
||||
private val demoFileDirectory: String = "src"
|
||||
private val demoFileNameWithoutExtension: String = "Welcome"
|
||||
private val demoFileName: String = "$demoFileNameWithoutExtension.java"
|
||||
|
||||
private val uiSettings get() = UISettings.getInstance()
|
||||
|
||||
override val properties: LessonProperties = LessonProperties(
|
||||
canStartInDumbMode = true,
|
||||
openFileAtStart = false
|
||||
)
|
||||
|
||||
override val testScriptProperties: TaskTestContext.TestScriptProperties = TaskTestContext.TestScriptProperties(skipTesting = true)
|
||||
|
||||
private var backupPopupLocation: Point? = null
|
||||
private var hideToolStripesPreference = false
|
||||
private var showNavigationBarPreference = true
|
||||
|
||||
@NlsSafe
|
||||
private var jdkAtStart: String = "undefined"
|
||||
|
||||
val sample: LessonSample = parseLessonSample("""
|
||||
class JavaOnboardingTourLesson : OnboardingTourLessonBase("java.onboarding") {
|
||||
override val demoFileExtension: String = "java"
|
||||
override val learningProjectName: String = "IdeaLearningProject"
|
||||
override val sample: LessonSample = parseLessonSample("""
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
|
||||
@@ -128,459 +37,9 @@ class JavaOnboardingTourLesson : KLesson("java.onboarding", JavaLessonsBundle.me
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
override val lessonContent: LessonContext.() -> Unit = {
|
||||
prepareRuntimeTask {
|
||||
jdkAtStart = getCurrentJdkVersionString(project)
|
||||
useDelay = true
|
||||
invokeActionForFocusContext(getActionById("Stop"))
|
||||
val runManager = RunManager.getInstance(project)
|
||||
runManager.allSettings.forEach(runManager::removeConfiguration)
|
||||
override val completionStepExpectedCompletion: String = "length"
|
||||
|
||||
val root = ProjectUtils.getCurrentLearningProjectRoot()
|
||||
val srcDir = root.findChild(demoFileDirectory) ?: error("'src' directory not found.")
|
||||
if (srcDir.findChild(demoFileName) == null) invokeLater {
|
||||
runWriteAction {
|
||||
srcDir.createChildData(this, demoFileName)
|
||||
// todo: This file shows with .java extension in the Project view and this extension disappears when user open it
|
||||
// (because we fill the file after the user open it) Fill the file immediately in this place?
|
||||
}
|
||||
}
|
||||
}
|
||||
clearBreakpoints()
|
||||
|
||||
checkUiSettings()
|
||||
|
||||
projectTasks()
|
||||
|
||||
prepareSample(sample, checkSdkConfiguration = false)
|
||||
|
||||
openLearnToolwindow()
|
||||
|
||||
sdkConfigurationTasks()
|
||||
|
||||
waitIndexingTasks()
|
||||
|
||||
runTasks()
|
||||
|
||||
debugTasks()
|
||||
|
||||
completionSteps()
|
||||
|
||||
waitBeforeContinue(500)
|
||||
|
||||
contextActions()
|
||||
|
||||
waitBeforeContinue(500)
|
||||
|
||||
searchEverywhereTasks()
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.epilog",
|
||||
getCallBackActionId("CloseProject"),
|
||||
LessonUtil.returnToWelcomeScreenRemark(),
|
||||
LearningUiManager.addCallback { LearningUiManager.resetModulesView() }))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLessonEnd(project: Project, lessonEndInfo: LessonEndInfo) {
|
||||
prepareFeedbackData(project, lessonEndInfo)
|
||||
restorePopupPosition(project, SearchEverywhereManagerImpl.LOCATION_SETTINGS_KEY, backupPopupLocation)
|
||||
backupPopupLocation = null
|
||||
|
||||
uiSettings.hideToolStripes = hideToolStripesPreference
|
||||
uiSettings.showNavigationBar = showNavigationBarPreference
|
||||
uiSettings.fireUISettingsChanged()
|
||||
|
||||
if (!lessonEndInfo.lessonPassed) {
|
||||
LessonUtil.showFeedbackNotification(this, project)
|
||||
return
|
||||
}
|
||||
val dataContextPromise = DataManager.getInstance().dataContextFromFocusAsync
|
||||
invokeLater {
|
||||
val result = MessageDialogBuilder.yesNoCancel(JavaLessonsBundle.message("java.onboarding.finish.title"),
|
||||
JavaLessonsBundle.message("java.onboarding.finish.text",
|
||||
LessonUtil.returnToWelcomeScreenRemark()))
|
||||
.yesText(JavaLessonsBundle.message("java.onboarding.finish.exit"))
|
||||
.noText(JavaLessonsBundle.message("java.onboarding.finish.modules"))
|
||||
.icon(FeaturesTrainerIcons.PluginIcon)
|
||||
.show(project)
|
||||
|
||||
when (result) {
|
||||
Messages.YES -> invokeLater {
|
||||
LessonManager.instance.stopLesson()
|
||||
val closeAction = getActionById("CloseProject")
|
||||
dataContextPromise.onSuccess { context ->
|
||||
invokeLater {
|
||||
val event = AnActionEvent.createFromAnAction(closeAction, null, ActionPlaces.LEARN_TOOLWINDOW, context)
|
||||
ActionUtil.performActionDumbAwareWithCallbacks(closeAction, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
Messages.NO -> invokeLater {
|
||||
LearningUiManager.resetModulesView()
|
||||
}
|
||||
}
|
||||
if (result != Messages.YES) {
|
||||
LessonUtil.showFeedbackNotification(this, project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun prepareFeedbackData(project: Project, lessonEndInfo: LessonEndInfo) {
|
||||
val primaryLanguage = module.primaryLanguage
|
||||
if (primaryLanguage == null) {
|
||||
thisLogger().error("Onboarding lesson has no language support for some magical reason")
|
||||
return
|
||||
}
|
||||
val configPropertyName = getFeedbackProposedPropertyName(primaryLanguage)
|
||||
if (PropertiesComponent.getInstance().getBoolean(configPropertyName, false)) {
|
||||
return
|
||||
}
|
||||
|
||||
val jdkVersionsFuture = CompletableFuture<List<String>>()
|
||||
runBackgroundableTask(ProjectBundle.message("progress.title.detecting.sdks"), project, false) { indicator ->
|
||||
val jdkVersions = mutableListOf<String>()
|
||||
SdkDetector.getInstance().detectSdks(JavaSdk.getInstance(), indicator, object : SdkDetector.DetectedSdkListener {
|
||||
override fun onSdkDetected(type: SdkType, version: String, home: String) {
|
||||
jdkVersions.add(version)
|
||||
}
|
||||
|
||||
override fun onSearchCompleted() {
|
||||
jdkVersionsFuture.complete(jdkVersions)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
val currentJdkVersion: @NlsSafe String = getCurrentJdkVersionString(project)
|
||||
|
||||
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) {
|
||||
override val feedbackReportId = "idea_onboarding_tour"
|
||||
|
||||
override val additionalFeedbackFormatVersion: Int = 1
|
||||
|
||||
private val jdkVersions: List<String>? by lazy {
|
||||
if (jdkVersionsFuture.isDone) jdkVersionsFuture.get() else null
|
||||
}
|
||||
|
||||
override val addAdditionalSystemData: JsonObjectBuilder.() -> Unit = {
|
||||
put("jdk_at_start", jdkAtStart)
|
||||
put("current_jdk", currentJdkVersion)
|
||||
put("language_level", currentLanguageLevel)
|
||||
put("found_jdk", buildJsonArray {
|
||||
for (version in jdkVersions ?: emptyList()) {
|
||||
add(JsonPrimitive(version))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override val addRowsForUserAgreement: Panel.() -> Unit = {
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.found.jdks")) {
|
||||
val versions: @NlsSafe String = jdkVersions?.joinToString("\n") ?: "none"
|
||||
cell(MultiLineLabel(versions))
|
||||
}
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.jdk.at.start")) {
|
||||
label(jdkAtStart)
|
||||
}
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.current.jdk")) {
|
||||
label(currentJdkVersion)
|
||||
}
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.lang.level")) {
|
||||
label(currentLanguageLevel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun feedbackHasBeenProposed() {
|
||||
PropertiesComponent.getInstance().setValue(configPropertyName, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentJdkVersionString(project: Project): String {
|
||||
return JavaProjectUtil.getEffectiveJdk(project)?.let { JavaSdk.getInstance().getVersionString(it) } ?: "none"
|
||||
}
|
||||
|
||||
private fun LessonContext.debugTasks() {
|
||||
clearBreakpoints()
|
||||
|
||||
var logicalPosition = LogicalPosition(0, 0)
|
||||
prepareRuntimeTask {
|
||||
logicalPosition = editor.offsetToLogicalPosition(sample.startOffset)
|
||||
}
|
||||
caret(sample.startOffset)
|
||||
|
||||
toggleBreakpointTask(sample, { logicalPosition }, checkLine = false) {
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.click.here"),
|
||||
LearningBalloonConfig(Balloon.Position.below, width = 0, cornerToPointerDistance = 20))
|
||||
text(JavaLessonsBundle.message("java.onboarding.toggle.breakpoint.1",
|
||||
code("6.5"), code("findAverage"), code("26")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.toggle.breakpoint.2"))
|
||||
}
|
||||
|
||||
highlightButtonById("Debug", highlightInside = false, usePulsation = false)
|
||||
|
||||
actionTask("Debug") {
|
||||
showBalloonOnHighlightingComponent(JavaLessonsBundle.message("java.onboarding.balloon.start.debugging"))
|
||||
restoreState {
|
||||
lineWithBreakpoints() != setOf(logicalPosition.line)
|
||||
}
|
||||
restoreIfModified(sample)
|
||||
JavaLessonsBundle.message("java.onboarding.start.debugging", icon(AllIcons.Actions.StartDebugger))
|
||||
}
|
||||
|
||||
lateinit var debuggerGotItTaskId: TaskContext.TaskId
|
||||
task {
|
||||
debuggerGotItTaskId = taskId
|
||||
}
|
||||
|
||||
highlightDebugActionsToolbar()
|
||||
|
||||
task {
|
||||
rehighlightPreviousUi = true
|
||||
gotItStep(Balloon.Position.above, width = 0,
|
||||
JavaLessonsBundle.message("java.onboarding.balloon.about.debug.panel",
|
||||
strong(UIBundle.message("tool.window.name.debug")),
|
||||
strong(LessonsBundle.message("debug.workflow.lesson.name"))))
|
||||
restoreByUi(debuggerGotItTaskId)
|
||||
}
|
||||
|
||||
highlightButtonById("Stop", highlightInside = false, usePulsation = false)
|
||||
task {
|
||||
val position = if (UIExperiment.isNewDebuggerUIEnabled()) Balloon.Position.above else Balloon.Position.atRight
|
||||
showBalloonOnHighlightingComponent(JavaLessonsBundle.message("java.onboarding.balloon.stop.debugging"),
|
||||
position) { list -> list.maxByOrNull { it.locationOnScreen.y } }
|
||||
text(JavaLessonsBundle.message("java.onboarding.stop.debugging", icon(AllIcons.Actions.Suspend)))
|
||||
restoreIfModified(sample)
|
||||
stateCheck {
|
||||
XDebuggerManager.getInstance(project).currentSession == null
|
||||
}
|
||||
}
|
||||
|
||||
prepareRuntimeTask {
|
||||
LearningUiHighlightingManager.clearHighlights()
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.waitIndexingTasks() {
|
||||
task {
|
||||
triggerAndBorderHighlight().component { progress: NonOpaquePanel ->
|
||||
progress.javaClass.name.contains("InlineProgressPanel")
|
||||
}
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.indexing.description"))
|
||||
text(JavaLessonsBundle.message("java.onboarding.wait.indexing"), LearningBalloonConfig(Balloon.Position.above, 0))
|
||||
waitSmartModeStep()
|
||||
}
|
||||
|
||||
waitBeforeContinue(300)
|
||||
|
||||
prepareRuntimeTask {
|
||||
LearningUiHighlightingManager.clearHighlights()
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.runTasks() {
|
||||
highlightRunToolbar(highlightInside = false, usePulsation = false)
|
||||
|
||||
task {
|
||||
triggerUI {
|
||||
clearPreviousHighlights = false
|
||||
}.component { ui: ActionButton -> ActionManager.getInstance().getId(ui.action) == "Run" }
|
||||
}
|
||||
|
||||
task {
|
||||
val introductionText = JavaLessonsBundle.message("java.onboarding.temporary.configuration.description",
|
||||
strong(ActionsBundle.actionText("NewUiRunWidget")),
|
||||
icon(AllIcons.Actions.Execute),
|
||||
icon(AllIcons.Actions.StartDebugger))
|
||||
val runOptionsText = if (PlatformUtils.isIdeaUltimate()) {
|
||||
JavaLessonsBundle.message("java.onboarding.run.options.ultimate",
|
||||
icon(AllIcons.Actions.Profile),
|
||||
icon(AllIcons.General.RunWithCoverage),
|
||||
icon(AllIcons.Actions.More))
|
||||
}
|
||||
else {
|
||||
JavaLessonsBundle.message("java.onboarding.run.options.community",
|
||||
icon(AllIcons.General.RunWithCoverage),
|
||||
icon(AllIcons.Actions.More))
|
||||
}
|
||||
|
||||
text("$introductionText $runOptionsText")
|
||||
text(JavaLessonsBundle.message("java.onboarding.run.sample", icon(AllIcons.Actions.Execute), action("Run")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.run.sample.balloon", icon(AllIcons.Actions.Execute), action("Run")),
|
||||
LearningBalloonConfig(Balloon.Position.below, 0))
|
||||
checkToolWindowState("Run", true)
|
||||
restoreIfModified(sample)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.openLearnToolwindow() {
|
||||
task {
|
||||
triggerAndBorderHighlight().component { stripe: ActionButton ->
|
||||
stripe.action.templateText == LearnBundle.message("toolwindow.stripe.Learn")
|
||||
}
|
||||
}
|
||||
|
||||
task {
|
||||
openLearnTaskId = taskId
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.open.learn.toolbar", strong(LearnBundle.message("toolwindow.stripe.Learn"))),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, width = 0, duplicateMessage = true))
|
||||
stateCheck {
|
||||
ToolWindowManager.getInstance(project).getToolWindow("Learn")?.isVisible == true
|
||||
}
|
||||
restoreIfModified(sample)
|
||||
}
|
||||
|
||||
prepareRuntimeTask {
|
||||
LearningUiHighlightingManager.clearHighlights()
|
||||
requestEditorFocus()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun LessonContext.checkUiSettings() {
|
||||
hideToolStripesPreference = uiSettings.hideToolStripes
|
||||
showNavigationBarPreference = uiSettings.showNavigationBar
|
||||
|
||||
showInvalidDebugLayoutWarning()
|
||||
|
||||
if (!hideToolStripesPreference && (showNavigationBarPreference || uiSettings.showMainToolbar)) {
|
||||
// a small hack to have same tasks count. It is needed to track statistics result.
|
||||
task { }
|
||||
task { }
|
||||
return
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.change.ui.settings"))
|
||||
proceedLink()
|
||||
}
|
||||
|
||||
prepareRuntimeTask {
|
||||
uiSettings.hideToolStripes = false
|
||||
uiSettings.showNavigationBar = true
|
||||
uiSettings.fireUISettingsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.projectTasks() {
|
||||
prepareRuntimeTask {
|
||||
LessonUtil.hideStandardToolwindows(project)
|
||||
}
|
||||
|
||||
task {
|
||||
triggerAndBorderHighlight().component { stripe: ActionButton ->
|
||||
stripe.action.templateText == IdeUICustomization.getInstance().getProjectViewTitle(project)
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var openProjectViewTask: TaskContext.TaskId
|
||||
task {
|
||||
openProjectViewTask = taskId
|
||||
var projectDirExpanded = false
|
||||
|
||||
text(JavaLessonsBundle.message("java.onboarding.project.view.description",
|
||||
action("ActivateProjectToolWindow")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.project.view"),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, width = 0, cornerToPointerDistance = 8))
|
||||
triggerUI().treeItem { tree: JTree, path: TreePath ->
|
||||
val result = path.pathCount >= 2 && path.getPathComponent(1).isToStringContains("IdeaLearningProject")
|
||||
if (result) {
|
||||
if (!projectDirExpanded) {
|
||||
invokeLater { tree.expandPath(path) }
|
||||
}
|
||||
projectDirExpanded = true
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
task {
|
||||
var srcDirCollapsed = false
|
||||
triggerAndBorderHighlight().treeItem { tree: JTree, path: TreePath ->
|
||||
val result = path.pathCount >= 3
|
||||
&& path.getPathComponent(1).isToStringContains("IdeaLearningProject")
|
||||
&& path.getPathComponent(2).isToStringContains(demoFileDirectory)
|
||||
if (result) {
|
||||
if (!srcDirCollapsed) {
|
||||
invokeLater { tree.collapsePath(path) }
|
||||
}
|
||||
srcDirCollapsed = true
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fun isDemoFilePath(path: TreePath) =
|
||||
path.pathCount >= 4 && path.getPathComponent(3).isToStringContains(demoFileNameWithoutExtension)
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.source.directory", strong(demoFileDirectory)),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, duplicateMessage = true, width = 0))
|
||||
triggerAndBorderHighlight().treeItem { _: JTree, path: TreePath ->
|
||||
isDemoFilePath(path)
|
||||
}
|
||||
restoreByUi(openProjectViewTask)
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.open.file", strong(demoFileName)),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, duplicateMessage = true, width = 0))
|
||||
stateCheck l@{
|
||||
if (FileEditorManager.getInstance(project).selectedTextEditor == null) return@l false
|
||||
virtualFile.name == demoFileName
|
||||
}
|
||||
restoreState {
|
||||
(previous.ui as? JTree)?.takeIf { tree ->
|
||||
TreeUtil.visitVisibleRows(tree, TreeVisitor { path ->
|
||||
if (isDemoFilePath(path)) TreeVisitor.Action.INTERRUPT else TreeVisitor.Action.CONTINUE
|
||||
}) != null
|
||||
}?.isShowing?.not() ?: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.completionSteps() {
|
||||
prepareRuntimeTask {
|
||||
setSample(sample.insertAtPosition(2, " / values<caret>"))
|
||||
FocusManagerImpl.getInstance(project).requestFocusInProject(editor.contentComponent, project)
|
||||
}
|
||||
|
||||
task {
|
||||
val textToFind = "result / values"
|
||||
triggerOnEditorText(textToFind, centerOffset = textToFind.length)
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.type.division",
|
||||
code(" / values")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.completion", code(".")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.completion.balloon", code(".")),
|
||||
LearningBalloonConfig(Balloon.Position.below, width = 0))
|
||||
triggerAndBorderHighlight().listItem { // no highlighting
|
||||
it.isToStringContains("length")
|
||||
}
|
||||
proposeRestoreForInvalidText(".")
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.choose.values.item",
|
||||
code("length"), action("EditorChooseLookupItem")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.completion.tip", action("CodeCompletion")))
|
||||
stateCheck {
|
||||
checkEditorModification(sample, modificationPositionId = 2, needChange = "/values.length")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.contextActions() {
|
||||
override fun LessonContext.contextActions() {
|
||||
val quickFixMessage = InspectionGadgetsBundle.message("foreach.replace.quickfix")
|
||||
caret(sample.getPosition(3))
|
||||
|
||||
@@ -643,50 +102,4 @@ class JavaOnboardingTourLesson : KLesson("java.onboarding", JavaLessonsBundle.me
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.searchEverywhereTasks() {
|
||||
val toggleCase = ActionsBundle.message("action.EditorToggleCase.text")
|
||||
caret("AVERAGE", select = true)
|
||||
task("SearchEverywhere") {
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.search.everywhere.1",
|
||||
strong(toggleCase), code("AVERAGE")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.search.everywhere.2",
|
||||
LessonUtil.rawKeyStroke(KeyEvent.VK_SHIFT), LessonUtil.actionName(it)))
|
||||
triggerAndBorderHighlight().component { ui: ExtendableTextField ->
|
||||
UIUtil.getParentOfType(SearchEverywhereUI::class.java, ui) != null
|
||||
}
|
||||
restoreIfModifiedOrMoved()
|
||||
}
|
||||
|
||||
task {
|
||||
transparentRestore = true
|
||||
before {
|
||||
if (backupPopupLocation != null) return@before
|
||||
val ui = previous.ui ?: return@before
|
||||
val popupWindow = UIUtil.getParentOfType(JWindow::class.java, ui) ?: return@before
|
||||
val oldPopupLocation = WindowStateService.getInstance(project).getLocation(SearchEverywhereManagerImpl.LOCATION_SETTINGS_KEY)
|
||||
if (adjustSearchEverywherePosition(popupWindow, "of array ") || LessonUtil.adjustPopupPosition(project, popupWindow)) {
|
||||
backupPopupLocation = oldPopupLocation
|
||||
}
|
||||
}
|
||||
text(JavaLessonsBundle.message("java.onboarding.search.everywhere.description",
|
||||
code("AVERAGE"), code(JavaLessonsBundle.message("toggle.case.part"))))
|
||||
triggerAndBorderHighlight().listItem { item ->
|
||||
val value = (item as? GotoActionModel.MatchedValue)?.value
|
||||
(value as? GotoActionModel.ActionWrapper)?.action is ToggleCaseAction
|
||||
}
|
||||
restoreByUi()
|
||||
restoreIfModifiedOrMoved()
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.apply.action", strong(toggleCase), LessonUtil.rawEnter()))
|
||||
stateCheck {
|
||||
editor.document.text.contains("\"average")
|
||||
}
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
}
|
||||
|
||||
text(JavaLessonsBundle.message("java.onboarding.case.changed"))
|
||||
}
|
||||
}
|
||||
+615
@@ -0,0 +1,615 @@
|
||||
// Copyright 2000-2023 JetBrains s.r.o. and contributors. Use of this source code is governed by the Apache 2.0 license.
|
||||
package com.intellij.java.ift.lesson.essential
|
||||
|
||||
import com.intellij.execution.RunManager
|
||||
import com.intellij.execution.ui.UIExperiment
|
||||
import com.intellij.icons.AllIcons
|
||||
import com.intellij.ide.DataManager
|
||||
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereManagerImpl
|
||||
import com.intellij.ide.actions.searcheverywhere.SearchEverywhereUI
|
||||
import com.intellij.ide.ui.UISettings
|
||||
import com.intellij.ide.util.PropertiesComponent
|
||||
import com.intellij.ide.util.gotoByName.GotoActionModel
|
||||
import com.intellij.idea.ActionsBundle
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import com.intellij.java.ift.JavaProjectUtil
|
||||
import com.intellij.openapi.actionSystem.ActionManager
|
||||
import com.intellij.openapi.actionSystem.ActionPlaces
|
||||
import com.intellij.openapi.actionSystem.AnActionEvent
|
||||
import com.intellij.openapi.actionSystem.ex.ActionUtil
|
||||
import com.intellij.openapi.actionSystem.impl.ActionButton
|
||||
import com.intellij.openapi.application.invokeLater
|
||||
import com.intellij.openapi.application.runWriteAction
|
||||
import com.intellij.openapi.diagnostic.thisLogger
|
||||
import com.intellij.openapi.editor.LogicalPosition
|
||||
import com.intellij.openapi.editor.actions.ToggleCaseAction
|
||||
import com.intellij.openapi.fileEditor.FileEditorManager
|
||||
import com.intellij.openapi.module.LanguageLevelUtil
|
||||
import com.intellij.openapi.module.ModuleManager
|
||||
import com.intellij.openapi.progress.runBackgroundableTask
|
||||
import com.intellij.openapi.project.Project
|
||||
import com.intellij.openapi.project.ProjectBundle
|
||||
import com.intellij.openapi.projectRoots.JavaSdk
|
||||
import com.intellij.openapi.projectRoots.SdkType
|
||||
import com.intellij.openapi.roots.ui.configuration.SdkDetector
|
||||
import com.intellij.openapi.ui.MessageDialogBuilder
|
||||
import com.intellij.openapi.ui.Messages
|
||||
import com.intellij.openapi.ui.ex.MultiLineLabel
|
||||
import com.intellij.openapi.ui.popup.Balloon
|
||||
import com.intellij.openapi.util.NlsSafe
|
||||
import com.intellij.openapi.util.WindowStateService
|
||||
import com.intellij.openapi.wm.ToolWindowManager
|
||||
import com.intellij.openapi.wm.impl.FocusManagerImpl
|
||||
import com.intellij.ui.IdeUICustomization
|
||||
import com.intellij.ui.UIBundle
|
||||
import com.intellij.ui.components.fields.ExtendableTextField
|
||||
import com.intellij.ui.components.panels.NonOpaquePanel
|
||||
import com.intellij.ui.dsl.builder.Panel
|
||||
import com.intellij.ui.tree.TreeVisitor
|
||||
import com.intellij.util.PlatformUtils
|
||||
import com.intellij.util.ui.UIUtil
|
||||
import com.intellij.util.ui.tree.TreeUtil
|
||||
import com.intellij.xdebugger.XDebuggerManager
|
||||
import kotlinx.serialization.json.JsonObjectBuilder
|
||||
import kotlinx.serialization.json.JsonPrimitive
|
||||
import kotlinx.serialization.json.buildJsonArray
|
||||
import kotlinx.serialization.json.put
|
||||
import training.FeaturesTrainerIcons
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.adjustSearchEverywherePosition
|
||||
import training.dsl.LessonUtil.checkEditorModification
|
||||
import training.dsl.LessonUtil.restoreIfModified
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.dsl.LessonUtil.restorePopupPosition
|
||||
import training.learn.LearnBundle
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.course.KLesson
|
||||
import training.learn.course.LessonProperties
|
||||
import training.learn.lesson.LessonManager
|
||||
import training.learn.lesson.general.run.clearBreakpoints
|
||||
import training.learn.lesson.general.run.toggleBreakpointTask
|
||||
import training.project.ProjectUtils
|
||||
import training.ui.LearningUiHighlightingManager
|
||||
import training.ui.LearningUiManager
|
||||
import training.ui.getFeedbackProposedPropertyName
|
||||
import training.util.*
|
||||
import java.awt.Point
|
||||
import java.awt.event.KeyEvent
|
||||
import java.util.concurrent.CompletableFuture
|
||||
import javax.swing.JTree
|
||||
import javax.swing.JWindow
|
||||
import javax.swing.tree.TreePath
|
||||
|
||||
abstract class OnboardingTourLessonBase(id: String) : KLesson(id, JavaLessonsBundle.message("java.onboarding.lesson.name")) {
|
||||
private lateinit var openLearnTaskId: TaskContext.TaskId
|
||||
private var useDelay: Boolean = false
|
||||
|
||||
private val demoFileDirectory: String = "src"
|
||||
private val demoFileNameWithoutExtension: String = "Welcome"
|
||||
abstract val demoFileExtension: String
|
||||
private val demoFileName: String
|
||||
get() = "$demoFileNameWithoutExtension.$demoFileExtension"
|
||||
|
||||
private val uiSettings get() = UISettings.getInstance()
|
||||
|
||||
override val properties: LessonProperties = LessonProperties(
|
||||
canStartInDumbMode = true,
|
||||
openFileAtStart = false
|
||||
)
|
||||
|
||||
override val testScriptProperties: TaskTestContext.TestScriptProperties = TaskTestContext.TestScriptProperties(skipTesting = true)
|
||||
|
||||
private var backupPopupLocation: Point? = null
|
||||
private var hideToolStripesPreference = false
|
||||
private var showNavigationBarPreference = true
|
||||
|
||||
@NlsSafe
|
||||
private var jdkAtStart: String = "undefined"
|
||||
|
||||
abstract val sample: LessonSample
|
||||
|
||||
abstract val learningProjectName: String
|
||||
|
||||
override val lessonContent: LessonContext.() -> Unit = {
|
||||
prepareRuntimeTask {
|
||||
jdkAtStart = getCurrentJdkVersionString(project)
|
||||
useDelay = true
|
||||
invokeActionForFocusContext(getActionById("Stop"))
|
||||
val runManager = RunManager.getInstance(project)
|
||||
runManager.allSettings.forEach(runManager::removeConfiguration)
|
||||
|
||||
val root = ProjectUtils.getCurrentLearningProjectRoot()
|
||||
val srcDir = root.findChild(demoFileDirectory) ?: error("'src' directory not found.")
|
||||
if (srcDir.findChild(demoFileName) == null) invokeLater {
|
||||
runWriteAction {
|
||||
srcDir.createChildData(this, demoFileName)
|
||||
// todo: This file shows with .java extension in the Project view and this extension disappears when user open it
|
||||
// (because we fill the file after the user open it) Fill the file immediately in this place?
|
||||
}
|
||||
}
|
||||
}
|
||||
clearBreakpoints()
|
||||
|
||||
checkUiSettings()
|
||||
|
||||
projectTasks()
|
||||
|
||||
prepareSample(sample, checkSdkConfiguration = false)
|
||||
|
||||
openLearnToolwindow()
|
||||
|
||||
sdkConfigurationTasks()
|
||||
|
||||
waitIndexingTasks()
|
||||
|
||||
runTasks()
|
||||
|
||||
debugTasks()
|
||||
|
||||
completionSteps()
|
||||
|
||||
waitBeforeContinue(500)
|
||||
|
||||
contextActions()
|
||||
|
||||
waitBeforeContinue(500)
|
||||
|
||||
searchEverywhereTasks()
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.epilog",
|
||||
getCallBackActionId("CloseProject"),
|
||||
LessonUtil.returnToWelcomeScreenRemark(),
|
||||
LearningUiManager.addCallback { LearningUiManager.resetModulesView() }))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLessonEnd(project: Project, lessonEndInfo: LessonEndInfo) {
|
||||
prepareFeedbackData(project, lessonEndInfo)
|
||||
restorePopupPosition(project, SearchEverywhereManagerImpl.LOCATION_SETTINGS_KEY, backupPopupLocation)
|
||||
backupPopupLocation = null
|
||||
|
||||
uiSettings.hideToolStripes = hideToolStripesPreference
|
||||
uiSettings.showNavigationBar = showNavigationBarPreference
|
||||
uiSettings.fireUISettingsChanged()
|
||||
|
||||
if (!lessonEndInfo.lessonPassed) {
|
||||
LessonUtil.showFeedbackNotification(this, project)
|
||||
return
|
||||
}
|
||||
val dataContextPromise = DataManager.getInstance().dataContextFromFocusAsync
|
||||
invokeLater {
|
||||
val result = MessageDialogBuilder.yesNoCancel(JavaLessonsBundle.message("java.onboarding.finish.title"),
|
||||
JavaLessonsBundle.message("java.onboarding.finish.text",
|
||||
LessonUtil.returnToWelcomeScreenRemark()))
|
||||
.yesText(JavaLessonsBundle.message("java.onboarding.finish.exit"))
|
||||
.noText(JavaLessonsBundle.message("java.onboarding.finish.modules"))
|
||||
.icon(FeaturesTrainerIcons.PluginIcon)
|
||||
.show(project)
|
||||
|
||||
when (result) {
|
||||
Messages.YES -> invokeLater {
|
||||
LessonManager.instance.stopLesson()
|
||||
val closeAction = getActionById("CloseProject")
|
||||
dataContextPromise.onSuccess { context ->
|
||||
invokeLater {
|
||||
val event = AnActionEvent.createFromAnAction(closeAction, null, ActionPlaces.LEARN_TOOLWINDOW, context)
|
||||
ActionUtil.performActionDumbAwareWithCallbacks(closeAction, event)
|
||||
}
|
||||
}
|
||||
}
|
||||
Messages.NO -> invokeLater {
|
||||
LearningUiManager.resetModulesView()
|
||||
}
|
||||
}
|
||||
if (result != Messages.YES) {
|
||||
LessonUtil.showFeedbackNotification(this, project)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getCurrentJdkVersionString(project: Project): String {
|
||||
return JavaProjectUtil.getEffectiveJdk(project)?.let { JavaSdk.getInstance().getVersionString(it) } ?: "none"
|
||||
}
|
||||
|
||||
private fun prepareFeedbackData(project: Project, lessonEndInfo: LessonEndInfo) {
|
||||
val primaryLanguage = module.primaryLanguage
|
||||
if (primaryLanguage == null) {
|
||||
thisLogger().error("Onboarding lesson has no language support for some magical reason")
|
||||
return
|
||||
}
|
||||
val configPropertyName = getFeedbackProposedPropertyName(primaryLanguage)
|
||||
if (PropertiesComponent.getInstance().getBoolean(configPropertyName, false)) {
|
||||
return
|
||||
}
|
||||
|
||||
val jdkVersionsFuture = CompletableFuture<List<String>>()
|
||||
runBackgroundableTask(ProjectBundle.message("progress.title.detecting.sdks"), project, false) { indicator ->
|
||||
val jdkVersions = mutableListOf<String>()
|
||||
SdkDetector.getInstance().detectSdks(JavaSdk.getInstance(), indicator, object : SdkDetector.DetectedSdkListener {
|
||||
override fun onSdkDetected(type: SdkType, version: String, home: String) {
|
||||
jdkVersions.add(version)
|
||||
}
|
||||
|
||||
override fun onSearchCompleted() {
|
||||
jdkVersionsFuture.complete(jdkVersions)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Suppress("HardCodedStringLiteral")
|
||||
val currentJdkVersion: @NlsSafe String = getCurrentJdkVersionString(project)
|
||||
|
||||
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) {
|
||||
override val feedbackReportId = "idea_onboarding_tour"
|
||||
|
||||
override val additionalFeedbackFormatVersion: Int = 1
|
||||
|
||||
private val jdkVersions: List<String>? by lazy {
|
||||
if (jdkVersionsFuture.isDone) jdkVersionsFuture.get() else null
|
||||
}
|
||||
|
||||
override val addAdditionalSystemData: JsonObjectBuilder.() -> Unit = {
|
||||
put("jdk_at_start", jdkAtStart)
|
||||
put("current_jdk", currentJdkVersion)
|
||||
put("language_level", currentLanguageLevel)
|
||||
put("found_jdk", buildJsonArray {
|
||||
for (version in jdkVersions ?: emptyList()) {
|
||||
add(JsonPrimitive(version))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override val addRowsForUserAgreement: Panel.() -> Unit = {
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.found.jdks")) {
|
||||
val versions: @NlsSafe String = jdkVersions?.joinToString("\n") ?: "none"
|
||||
cell(MultiLineLabel(versions))
|
||||
}
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.jdk.at.start")) {
|
||||
label(jdkAtStart)
|
||||
}
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.current.jdk")) {
|
||||
label(currentJdkVersion)
|
||||
}
|
||||
row(JavaLessonsBundle.message("java.onboarding.feedback.system.lang.level")) {
|
||||
label(currentLanguageLevel)
|
||||
}
|
||||
}
|
||||
|
||||
override fun feedbackHasBeenProposed() {
|
||||
PropertiesComponent.getInstance().setValue(configPropertyName, true, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.debugTasks() {
|
||||
clearBreakpoints()
|
||||
|
||||
var logicalPosition = LogicalPosition(0, 0)
|
||||
prepareRuntimeTask {
|
||||
logicalPosition = editor.offsetToLogicalPosition(sample.startOffset)
|
||||
}
|
||||
caret(sample.startOffset)
|
||||
|
||||
toggleBreakpointTask(sample, { logicalPosition }, checkLine = false) {
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.click.here"),
|
||||
LearningBalloonConfig(Balloon.Position.below, width = 0, cornerToPointerDistance = 20))
|
||||
text(JavaLessonsBundle.message("java.onboarding.toggle.breakpoint.1",
|
||||
code("6.5"), code("findAverage"), code("26")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.toggle.breakpoint.2"))
|
||||
}
|
||||
|
||||
highlightButtonById("Debug", highlightInside = false, usePulsation = false)
|
||||
|
||||
actionTask("Debug") {
|
||||
showBalloonOnHighlightingComponent(JavaLessonsBundle.message("java.onboarding.balloon.start.debugging"))
|
||||
restoreState {
|
||||
lineWithBreakpoints() != setOf(logicalPosition.line)
|
||||
}
|
||||
restoreIfModified(sample)
|
||||
JavaLessonsBundle.message("java.onboarding.start.debugging", icon(AllIcons.Actions.StartDebugger))
|
||||
}
|
||||
|
||||
lateinit var debuggerGotItTaskId: TaskContext.TaskId
|
||||
task {
|
||||
debuggerGotItTaskId = taskId
|
||||
}
|
||||
|
||||
highlightDebugActionsToolbar()
|
||||
|
||||
task {
|
||||
rehighlightPreviousUi = true
|
||||
gotItStep(Balloon.Position.above, width = 0,
|
||||
JavaLessonsBundle.message("java.onboarding.balloon.about.debug.panel",
|
||||
strong(UIBundle.message("tool.window.name.debug")),
|
||||
strong(LessonsBundle.message("debug.workflow.lesson.name"))))
|
||||
restoreByUi(debuggerGotItTaskId)
|
||||
}
|
||||
|
||||
highlightButtonById("Stop", highlightInside = false, usePulsation = false)
|
||||
task {
|
||||
val position = if (UIExperiment.isNewDebuggerUIEnabled()) Balloon.Position.above else Balloon.Position.atRight
|
||||
showBalloonOnHighlightingComponent(JavaLessonsBundle.message("java.onboarding.balloon.stop.debugging"),
|
||||
position) { list -> list.maxByOrNull { it.locationOnScreen.y } }
|
||||
text(JavaLessonsBundle.message("java.onboarding.stop.debugging", icon(AllIcons.Actions.Suspend)))
|
||||
restoreIfModified(sample)
|
||||
stateCheck {
|
||||
XDebuggerManager.getInstance(project).currentSession == null
|
||||
}
|
||||
}
|
||||
|
||||
prepareRuntimeTask {
|
||||
LearningUiHighlightingManager.clearHighlights()
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.waitIndexingTasks() {
|
||||
task {
|
||||
triggerAndBorderHighlight().component { progress: NonOpaquePanel ->
|
||||
progress.javaClass.name.contains("InlineProgressPanel")
|
||||
}
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.indexing.description"))
|
||||
text(JavaLessonsBundle.message("java.onboarding.wait.indexing"), LearningBalloonConfig(Balloon.Position.above, 0))
|
||||
waitSmartModeStep()
|
||||
}
|
||||
|
||||
waitBeforeContinue(300)
|
||||
|
||||
prepareRuntimeTask {
|
||||
LearningUiHighlightingManager.clearHighlights()
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.runTasks() {
|
||||
highlightRunToolbar(highlightInside = false, usePulsation = false)
|
||||
|
||||
task {
|
||||
triggerUI {
|
||||
clearPreviousHighlights = false
|
||||
}.component { ui: ActionButton -> ActionManager.getInstance().getId(ui.action) == "Run" }
|
||||
}
|
||||
|
||||
task {
|
||||
val introductionText = JavaLessonsBundle.message("java.onboarding.temporary.configuration.description",
|
||||
strong(ActionsBundle.actionText("NewUiRunWidget")),
|
||||
icon(AllIcons.Actions.Execute),
|
||||
icon(AllIcons.Actions.StartDebugger))
|
||||
val runOptionsText = if (PlatformUtils.isIdeaUltimate()) {
|
||||
JavaLessonsBundle.message("java.onboarding.run.options.ultimate",
|
||||
icon(AllIcons.Actions.Profile),
|
||||
icon(AllIcons.General.RunWithCoverage),
|
||||
icon(AllIcons.Actions.More))
|
||||
}
|
||||
else {
|
||||
JavaLessonsBundle.message("java.onboarding.run.options.community",
|
||||
icon(AllIcons.General.RunWithCoverage),
|
||||
icon(AllIcons.Actions.More))
|
||||
}
|
||||
|
||||
text("$introductionText $runOptionsText")
|
||||
text(JavaLessonsBundle.message("java.onboarding.run.sample", icon(AllIcons.Actions.Execute), action("Run")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.run.sample.balloon", icon(AllIcons.Actions.Execute), action("Run")),
|
||||
LearningBalloonConfig(Balloon.Position.below, 0))
|
||||
checkToolWindowState("Run", true)
|
||||
restoreIfModified(sample)
|
||||
}
|
||||
}
|
||||
|
||||
private fun LessonContext.openLearnToolwindow() {
|
||||
task {
|
||||
triggerAndBorderHighlight().component { stripe: ActionButton ->
|
||||
stripe.action.templateText == LearnBundle.message("toolwindow.stripe.Learn")
|
||||
}
|
||||
}
|
||||
|
||||
task {
|
||||
openLearnTaskId = taskId
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.open.learn.toolbar", strong(LearnBundle.message("toolwindow.stripe.Learn"))),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, width = 0, duplicateMessage = true))
|
||||
stateCheck {
|
||||
ToolWindowManager.getInstance(project).getToolWindow("Learn")?.isVisible == true
|
||||
}
|
||||
restoreIfModified(sample)
|
||||
}
|
||||
|
||||
prepareRuntimeTask {
|
||||
LearningUiHighlightingManager.clearHighlights()
|
||||
requestEditorFocus()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun LessonContext.checkUiSettings() {
|
||||
hideToolStripesPreference = uiSettings.hideToolStripes
|
||||
showNavigationBarPreference = uiSettings.showNavigationBar
|
||||
|
||||
showInvalidDebugLayoutWarning()
|
||||
|
||||
if (!hideToolStripesPreference && (showNavigationBarPreference || uiSettings.showMainToolbar)) {
|
||||
// a small hack to have same tasks count. It is needed to track statistics result.
|
||||
task { }
|
||||
task { }
|
||||
return
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.change.ui.settings"))
|
||||
proceedLink()
|
||||
}
|
||||
|
||||
prepareRuntimeTask {
|
||||
uiSettings.hideToolStripes = false
|
||||
uiSettings.showNavigationBar = true
|
||||
uiSettings.fireUISettingsChanged()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun LessonContext.projectTasks() {
|
||||
prepareRuntimeTask {
|
||||
LessonUtil.hideStandardToolwindows(project)
|
||||
}
|
||||
|
||||
task {
|
||||
triggerAndBorderHighlight().component { stripe: ActionButton ->
|
||||
stripe.action.templateText == IdeUICustomization.getInstance().getProjectViewTitle(project)
|
||||
}
|
||||
}
|
||||
|
||||
lateinit var openProjectViewTask: TaskContext.TaskId
|
||||
task {
|
||||
openProjectViewTask = taskId
|
||||
var projectDirExpanded = false
|
||||
|
||||
text(JavaLessonsBundle.message("java.onboarding.project.view.description",
|
||||
action("ActivateProjectToolWindow")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.project.view"),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, width = 0, cornerToPointerDistance = 8))
|
||||
triggerUI().treeItem { tree: JTree, path: TreePath ->
|
||||
val result = path.pathCount >= 2 && path.getPathComponent(1).isToStringContains(learningProjectName)
|
||||
if (result) {
|
||||
if (!projectDirExpanded) {
|
||||
invokeLater { tree.expandPath(path) }
|
||||
}
|
||||
projectDirExpanded = true
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
task {
|
||||
var srcDirCollapsed = false
|
||||
triggerAndBorderHighlight().treeItem { tree: JTree, path: TreePath ->
|
||||
val result = path.pathCount >= 3
|
||||
&& path.getPathComponent(1).isToStringContains(learningProjectName)
|
||||
&& path.getPathComponent(2).isToStringContains(demoFileDirectory)
|
||||
if (result) {
|
||||
if (!srcDirCollapsed) {
|
||||
invokeLater { tree.collapsePath(path) }
|
||||
}
|
||||
srcDirCollapsed = true
|
||||
}
|
||||
result
|
||||
}
|
||||
}
|
||||
|
||||
fun isDemoFilePath(path: TreePath) =
|
||||
path.pathCount >= 4 && path.getPathComponent(3).isToStringContains(demoFileNameWithoutExtension)
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.source.directory", strong(demoFileDirectory)),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, duplicateMessage = true, width = 0))
|
||||
triggerAndBorderHighlight().treeItem { _: JTree, path: TreePath ->
|
||||
isDemoFilePath(path)
|
||||
}
|
||||
restoreByUi(openProjectViewTask)
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.balloon.open.file", strong(demoFileName)),
|
||||
LearningBalloonConfig(Balloon.Position.atRight, duplicateMessage = true, width = 0))
|
||||
stateCheck l@{
|
||||
if (FileEditorManager.getInstance(project).selectedTextEditor == null) return@l false
|
||||
virtualFile.name == demoFileName
|
||||
}
|
||||
restoreState {
|
||||
(previous.ui as? JTree)?.takeIf { tree ->
|
||||
TreeUtil.visitVisibleRows(tree, TreeVisitor { path ->
|
||||
if (isDemoFilePath(path)) TreeVisitor.Action.INTERRUPT else TreeVisitor.Action.CONTINUE
|
||||
}) != null
|
||||
}?.isShowing?.not() ?: true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract val completionStepExpectedCompletion: String
|
||||
|
||||
private fun LessonContext.completionSteps() {
|
||||
prepareRuntimeTask {
|
||||
setSample(sample.insertAtPosition(2, " / values<caret>"))
|
||||
FocusManagerImpl.getInstance(project).requestFocusInProject(editor.contentComponent, project)
|
||||
}
|
||||
|
||||
task {
|
||||
val textToFind = "result / values"
|
||||
triggerOnEditorText(textToFind, centerOffset = textToFind.length)
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.type.division",
|
||||
code(" / values")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.completion", code(".")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.completion.balloon", code(".")),
|
||||
LearningBalloonConfig(Balloon.Position.below, width = 0))
|
||||
triggerAndBorderHighlight().listItem { // no highlighting
|
||||
it.isToStringContains(completionStepExpectedCompletion)
|
||||
}
|
||||
proposeRestoreForInvalidText(".")
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.choose.values.item",
|
||||
code(completionStepExpectedCompletion), action("EditorChooseLookupItem")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.completion.tip", action("CodeCompletion")))
|
||||
stateCheck {
|
||||
checkEditorModification(sample, modificationPositionId = 2, needChange = "/values.$completionStepExpectedCompletion")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun LessonContext.contextActions()
|
||||
|
||||
private fun LessonContext.searchEverywhereTasks() {
|
||||
val toggleCase = ActionsBundle.message("action.EditorToggleCase.text")
|
||||
caret("AVERAGE", select = true)
|
||||
task("SearchEverywhere") {
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.search.everywhere.1",
|
||||
strong(toggleCase), code("AVERAGE")))
|
||||
text(JavaLessonsBundle.message("java.onboarding.invoke.search.everywhere.2",
|
||||
LessonUtil.rawKeyStroke(KeyEvent.VK_SHIFT), LessonUtil.actionName(it)))
|
||||
triggerAndBorderHighlight().component { ui: ExtendableTextField ->
|
||||
UIUtil.getParentOfType(SearchEverywhereUI::class.java, ui) != null
|
||||
}
|
||||
restoreIfModifiedOrMoved()
|
||||
}
|
||||
|
||||
task {
|
||||
transparentRestore = true
|
||||
before {
|
||||
if (backupPopupLocation != null) return@before
|
||||
val ui = previous.ui ?: return@before
|
||||
val popupWindow = UIUtil.getParentOfType(JWindow::class.java, ui) ?: return@before
|
||||
val oldPopupLocation = WindowStateService.getInstance(project).getLocation(SearchEverywhereManagerImpl.LOCATION_SETTINGS_KEY)
|
||||
if (adjustSearchEverywherePosition(popupWindow, "of array ") || LessonUtil.adjustPopupPosition(project, popupWindow)) {
|
||||
backupPopupLocation = oldPopupLocation
|
||||
}
|
||||
}
|
||||
text(JavaLessonsBundle.message("java.onboarding.search.everywhere.description",
|
||||
code("AVERAGE"), code(JavaLessonsBundle.message("toggle.case.part"))))
|
||||
triggerAndBorderHighlight().listItem { item ->
|
||||
val value = (item as? GotoActionModel.MatchedValue)?.value
|
||||
(value as? GotoActionModel.ActionWrapper)?.action is ToggleCaseAction
|
||||
}
|
||||
restoreByUi()
|
||||
restoreIfModifiedOrMoved()
|
||||
}
|
||||
|
||||
task {
|
||||
text(JavaLessonsBundle.message("java.onboarding.apply.action", strong(toggleCase), LessonUtil.rawEnter()))
|
||||
stateCheck {
|
||||
editor.document.text.contains("\"average")
|
||||
}
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
}
|
||||
|
||||
text(JavaLessonsBundle.message("java.onboarding.case.changed"))
|
||||
}
|
||||
}
|
||||
@@ -25,5 +25,11 @@
|
||||
<orderEntry type="module" module-name="kotlin.jvm" />
|
||||
<orderEntry type="module" module-name="kotlin.idea" />
|
||||
<orderEntry type="module" module-name="kotlin.base.resources" />
|
||||
<orderEntry type="module" module-name="intellij.java" />
|
||||
<orderEntry type="module" module-name="intellij.platform.execution.impl" />
|
||||
<orderEntry type="module" module-name="intellij.platform.debugger" />
|
||||
<orderEntry type="module" module-name="intellij.platform.core.ui" />
|
||||
<orderEntry type="library" name="kotlinx-serialization-core" level="project" />
|
||||
<orderEntry type="library" name="kotlinx-serialization-json" level="project" />
|
||||
</component>
|
||||
</module>
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
|
||||
const val A_CONST = 10000
|
||||
const val B_CONST = 500
|
||||
|
||||
<select>fun calc() { Processor.process(A_CONST, B_CONST) }</select>
|
||||
|
||||
class Processor{
|
||||
companion
|
||||
object {
|
||||
fun process(a: Int, b: Int): Int { return a * b + A_CONST
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
import javax.swing.*
|
||||
|
||||
fun main() {
|
||||
val frame = JFrame("FrameDemo")
|
||||
frame.setSize(<caret>)
|
||||
|
||||
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
|
||||
frame.setVisible(true)
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
import java.text.DecimalFormat
|
||||
|
||||
fun main() {
|
||||
<caret>customFormat("###,###.###", 123456.789)
|
||||
customFormat("###.##", 123456.789)
|
||||
customFormat("000000.000", 123.78)
|
||||
customFormat("$###,###.###", 12345.67)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints a double value formatted according to a given pattern.
|
||||
*/
|
||||
fun customFormat(pattern: String, value: Double) {
|
||||
val myFormatter = DecimalFormat(pattern);
|
||||
val output = myFormatter.format(value);
|
||||
System.out.println("$value $pattern $output");
|
||||
}
|
||||
+4
-12
@@ -4,18 +4,10 @@ import java.io.InputStream
|
||||
* This is a sample class used in IDE Feature Trainer
|
||||
* for illustration properties
|
||||
*/
|
||||
class BufferedReader {
|
||||
private var inputStream: InputStream
|
||||
private var maxBufferSize = 1024
|
||||
|
||||
constructor(inputStream: InputStream) {
|
||||
this.inputStream = inputStream
|
||||
}
|
||||
|
||||
constructor(inputStream: InputStream, maxBufferSize: Int) {
|
||||
this.inputStream = inputStream
|
||||
this.maxBufferSize = maxBufferSize
|
||||
}
|
||||
class BufferedReader(
|
||||
private var inputStream: InputStream,
|
||||
private var maxBufferSize: Int = 1024
|
||||
) {
|
||||
|
||||
/**
|
||||
* @return one byte from input stream
|
||||
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
open class DerivedClass1 : SomeInterface {
|
||||
override fun foo(demo: FileStructureDemo) {
|
||||
demo.boo()
|
||||
}
|
||||
}
|
||||
|
||||
class SecondLevelClassA : DerivedClass1() {
|
||||
override fun foo(demo: FileStructureDemo) {
|
||||
demo.foo()
|
||||
}
|
||||
}
|
||||
|
||||
class SecondLevelClassB : DerivedClass1()
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
class DerivedClass2 : SomeInterface {
|
||||
override fun foo(demo: FileStructureDemo) {
|
||||
demo.foo()
|
||||
}
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
class InheritanceHierarchySample {
|
||||
|
||||
}
|
||||
|
||||
fun someEntryPoint(base: SomeInterface, demo: FileStructureDemo) {
|
||||
base.foo(demo)
|
||||
}
|
||||
+1
@@ -13,6 +13,7 @@ class QuadraticEquationsSolver {
|
||||
val x2 = (-b - sqrt(d)) / (2.0 * a)
|
||||
println("x1 = $x1, x2 = $x2")
|
||||
}
|
||||
|
||||
else -> println("x = ${-b / (2.0 * a)}")
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -1,4 +1,3 @@
|
||||
|
||||
fun main() {
|
||||
println("Hello World!")
|
||||
}
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
interface SomeInterface {
|
||||
fun foo(demo: FileStructureDemo)
|
||||
}
|
||||
|
||||
interface SomeDerivedInterface : SomeInterface
|
||||
+5
@@ -0,0 +1,5 @@
|
||||
package util
|
||||
|
||||
object Utils {
|
||||
val FRUITS = arrayOf("pineapple", "banana", "apple", "grapes", "mango", "melon", "peach", "orange")
|
||||
}
|
||||
+14
@@ -0,0 +1,14 @@
|
||||
package warehouse
|
||||
|
||||
fun main() {
|
||||
val warehouse = Warehouse()
|
||||
warehouse.addFruits("peach", 3)
|
||||
warehouse.addFruits("pineapple", 5)
|
||||
warehouse.addFruits("mango", 1)
|
||||
warehouse.addFruits("apple", 5)
|
||||
val result = warehouse.takeFruit("apple")
|
||||
if (result) {
|
||||
println("This apple was delicious!")
|
||||
}
|
||||
warehouse.printAllFruits()
|
||||
}
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
package warehouse
|
||||
|
||||
import util.Utils
|
||||
|
||||
class Warehouse {
|
||||
// Fruit name to amount of it in warehouse
|
||||
private val entry: MutableMap<String, Int> = HashMap() // Apple, banana, etc...
|
||||
|
||||
init {
|
||||
val availableFruits: Array<String> = Utils.FRUITS
|
||||
for (fruit in availableFruits) {
|
||||
entry[fruit] = 0
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param fruitName some fruit name from [Utils.FRUITS] (mango, apple...)
|
||||
*/
|
||||
fun addFruits(fruitName: String, quantity: Int) {
|
||||
val curQuantity = entry[fruitName]
|
||||
if (curQuantity != null) {
|
||||
entry[fruitName] = curQuantity + quantity
|
||||
} else {
|
||||
throw IllegalArgumentException("Not found fruit with name: $fruitName")
|
||||
}
|
||||
}
|
||||
|
||||
fun takeFruit(fruitName: String): Boolean {
|
||||
val curQuantity = entry[fruitName]
|
||||
requireNotNull(curQuantity) { "Not found fruit with name: $fruitName" }
|
||||
if (curQuantity > 0) {
|
||||
entry[fruitName] = curQuantity - 1
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun printAllFruits() {
|
||||
for ((key, value) in entry) {
|
||||
println("$key: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,3 +1,19 @@
|
||||
configure.kotlin.progress.title=Configure Kotlin
|
||||
|
||||
kotlin.basic.completion.choose.item=Choose the highlighted {0} item to complete the function call and place the caret inside the parenthesis.
|
||||
kotlin.onboarding.invoke.intention.for.warning.1=You have just fixed a bug, but you can make this code look even better. \
|
||||
IDEA highlights and adds a yellow bulb to the code lines that can be improved.
|
||||
kotlin.onboarding.invoke.intention.for.warning.2=Press {0} to preview the warnings and apply a quick-fix.
|
||||
kotlin.onboarding.invoke.intention.for.warning.balloon=Press {0} to show available quick-fixes
|
||||
kotlin.onboarding.select.fix=Apply the first item: {0}. In this case, the <strong>for-each</strong> loop will make code easier to understand.
|
||||
kotlin.onboarding.invoke.intention.for.code=Intentions also save your time and make coding easier. Let''s use an intention to reformat string concatenation. \
|
||||
Press {0} to show possible options.
|
||||
kotlin.onboarding.invoke.intention.for.code.balloon=Press {0} to show available intentions
|
||||
kotlin.onboarding.apply.intention=Select {0} and press {1}.
|
||||
|
||||
kotlin.basic.completion.choose.item=Choose the highlighted {0} item to complete the function call and place the caret inside the parenthesis.
|
||||
|
||||
# Next string does not require translation
|
||||
kotlin.refactoring.menu.inline.property.eng=Now let''s replace single usage of the {0} variable with an expression that defines it. \
|
||||
You can press {1} and filter the refactoring menu by <strong>ipr</strong> (<strong>i</strong>nline <strong>pr</strong>operty). \
|
||||
Choose this item or press {2}.
|
||||
kotlin.refactoring.menu.confirm.constant=Choose and type any new name you want to use for the constant. Then press {0} to confirm.
|
||||
+1
@@ -18,6 +18,7 @@ import java.nio.file.Path
|
||||
class KotlinLangSupport : JavaBasedLangSupport() {
|
||||
override val contentRootDirectoryName = "KotlinLearningProject"
|
||||
|
||||
override val defaultProductName: String = "IDEA"
|
||||
override val primaryLanguage: String = "kotlin"
|
||||
override val scratchFileName: String = "Learning.kt"
|
||||
|
||||
|
||||
+96
-5
@@ -1,22 +1,55 @@
|
||||
// Copyright 2000-2022 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 org.jetbrains.kotlin.training.ift
|
||||
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import com.intellij.util.PlatformUtils
|
||||
import org.jetbrains.kotlin.idea.KotlinLanguage
|
||||
import org.jetbrains.kotlin.training.ift.lesson.basic.KotlinBasicCompletionLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.basic.KotlinContextActionsLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.basic.KotlinSelectLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.basic.KotlinSurroundAndUnwrapLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.completion.KotlinCompletionWithTabLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.completion.KotlinPostfixCompletionLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.completion.KotlinSmartTypeCompletionLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.essential.KotlinOnboardingTourLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.navigation.KotlinDeclarationAndUsagesLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.navigation.KotlinFileStructureLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.navigation.KotlinRecentFilesLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.navigation.KotlinSearchEverywhereLesson
|
||||
import org.jetbrains.kotlin.training.ift.lesson.refactorings.KotlinRefactoringMenuLesson
|
||||
import training.dsl.LessonUtil
|
||||
import training.learn.CourseManager
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.course.LearningCourseBase
|
||||
import training.learn.course.LearningModule
|
||||
import training.learn.course.LessonType
|
||||
import training.learn.lesson.general.*
|
||||
import training.learn.lesson.general.assistance.CodeFormatLesson
|
||||
import training.learn.lesson.general.assistance.LocalHistoryLesson
|
||||
import training.learn.lesson.general.assistance.ParameterInfoLesson
|
||||
import training.learn.lesson.general.assistance.QuickPopupsLesson
|
||||
import training.learn.lesson.general.navigation.FindInFilesLesson
|
||||
|
||||
class KotlinLearningCourse : LearningCourseBase(KotlinLanguage.INSTANCE.id) {
|
||||
override fun modules() = stableModules()
|
||||
override fun modules() = onboardingTour() + stableModules() + CourseManager.instance.findCommonModules("Git")
|
||||
|
||||
private val isOnboardingLessonEnabled: Boolean
|
||||
get() = PlatformUtils.isIdeaCommunity() || PlatformUtils.isIdeaUltimate()
|
||||
|
||||
private fun onboardingTour() = if (isOnboardingLessonEnabled) listOf(
|
||||
LearningModule(
|
||||
id = "Kotlin.Onboarding",
|
||||
name = JavaLessonsBundle.message("java.onboarding.module.name"),
|
||||
description = JavaLessonsBundle.message("java.onboarding.module.description", LessonUtil.productName),
|
||||
primaryLanguage = langSupport,
|
||||
moduleType = LessonType.PROJECT
|
||||
) {
|
||||
listOf(
|
||||
KotlinOnboardingTourLesson()
|
||||
)
|
||||
}
|
||||
)
|
||||
else emptyList()
|
||||
|
||||
private fun stableModules() = listOf(
|
||||
LearningModule(
|
||||
@@ -59,7 +92,44 @@ class KotlinLearningCourse : LearningCourseBase(KotlinLanguage.INSTANCE.id) {
|
||||
primaryLanguage = langSupport,
|
||||
moduleType = LessonType.SINGLE_EDITOR // todo: change to SCRATCH when KTIJ-20742 will be resolved
|
||||
) {
|
||||
listOf(KotlinBasicCompletionLesson())
|
||||
listOf(
|
||||
KotlinBasicCompletionLesson(),
|
||||
KotlinSmartTypeCompletionLesson(),
|
||||
KotlinPostfixCompletionLesson(),
|
||||
// TODO: KotlinStatementCompletionLesson
|
||||
KotlinCompletionWithTabLesson()
|
||||
)
|
||||
},
|
||||
LearningModule(
|
||||
id = "Kotlin.Refactorings",
|
||||
name = LessonsBundle.message("refactorings.module.name"),
|
||||
description = LessonsBundle.message("refactorings.module.description"),
|
||||
primaryLanguage = langSupport,
|
||||
moduleType = LessonType.SINGLE_EDITOR
|
||||
) {
|
||||
fun ls(sampleName: String) = loadSample("Refactorings/$sampleName")
|
||||
listOf(
|
||||
// TODO: KotlinRenameLesson(),
|
||||
// TODO: ExtractVariableFromBubbleLesson(ls("ExtractVariable.java.sample")),
|
||||
// TODO: KotlinExtractMethodCocktailSortLesson(),
|
||||
KotlinRefactoringMenuLesson(),
|
||||
)
|
||||
},
|
||||
LearningModule(
|
||||
id = "Kotlin.CodeAssistance",
|
||||
name = LessonsBundle.message("code.assistance.module.name"),
|
||||
description = LessonsBundle.message("code.assistance.module.description"),
|
||||
primaryLanguage = langSupport,
|
||||
moduleType = LessonType.SINGLE_EDITOR
|
||||
) {
|
||||
fun ls(sampleName: String) = loadSample("CodeAssistance/$sampleName")
|
||||
listOf(
|
||||
LocalHistoryLesson(),
|
||||
CodeFormatLesson(ls("CodeFormat.kt.sample"), true),
|
||||
ParameterInfoLesson(ls("ParameterInfo.kt.sample")),
|
||||
QuickPopupsLesson(ls("QuickPopups.kt.sample")),
|
||||
// TODO: KotlinEditorCodingAssistanceLesson(ls("EditorCodingAssistance.java.sample")),
|
||||
)
|
||||
},
|
||||
LearningModule(
|
||||
id = "Kotlin.Navigation",
|
||||
@@ -70,12 +140,27 @@ class KotlinLearningCourse : LearningCourseBase(KotlinLanguage.INSTANCE.id) {
|
||||
) {
|
||||
listOf(
|
||||
KotlinSearchEverywhereLesson(),
|
||||
FindInFilesLesson("src/warehouse/FindInFilesSample.kt"),
|
||||
KotlinFileStructureLesson(),
|
||||
KotlinDeclarationAndUsagesLesson(),
|
||||
// TODO: KotlinInheritanceHierarchyLesson
|
||||
KotlinRecentFilesLesson()
|
||||
// TODO: KotlinOccurrencesLesson
|
||||
)
|
||||
}
|
||||
},
|
||||
/*LearningModule(id = "Kotlin.RunAndDebug",
|
||||
name = LessonsBundle.message("run.debug.module.name"),
|
||||
description = LessonsBundle.message("run.debug.module.description"),
|
||||
primaryLanguage = langSupport,
|
||||
moduleType = LessonType.SINGLE_EDITOR) {
|
||||
listOf(
|
||||
// TODO: KotlinRunConfigurationLesson(),
|
||||
// TODO: KotlinDebugLesson(),
|
||||
)
|
||||
},*/
|
||||
)
|
||||
|
||||
override fun getLessonIdToTipsMap(): Map<String, List<String>> = mapOf(
|
||||
override fun getLessonIdToTipsMap(): Map<String, List<String>> = mutableMapOf(
|
||||
// Essential
|
||||
"context.actions" to listOf("ContextActions"),
|
||||
"Actions" to listOf("find_action", "GoToAction"),
|
||||
@@ -94,6 +179,12 @@ class KotlinLearningCourse : LearningCourseBase(KotlinLanguage.INSTANCE.id) {
|
||||
|
||||
// Navigation
|
||||
"Search everywhere" to listOf("SearchEverywhere", "GoToClass", "search_everywhere_general"),
|
||||
"Find in files" to listOf("FindReplaceToggle", "FindInPath"),
|
||||
"File structure" to listOf("FileStructurePopup"),
|
||||
)
|
||||
).also { map ->
|
||||
val gitCourse = CourseManager.instance.findCommonCourseById("Git")
|
||||
if (gitCourse != null) {
|
||||
map.putAll(gitCourse.getLessonIdToTipsMap())
|
||||
}
|
||||
}
|
||||
}
|
||||
+19
@@ -0,0 +1,19 @@
|
||||
// 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.kotlin.training.ift.lesson.completion
|
||||
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.parseLessonSample
|
||||
import training.learn.lesson.general.CompletionWithTabLesson
|
||||
|
||||
class KotlinCompletionWithTabLesson :
|
||||
CompletionWithTabLesson("DO_NOTHING_ON_CLOSE") {
|
||||
override val sample: LessonSample = parseLessonSample("""import javax.swing.*
|
||||
|
||||
fun main() {
|
||||
val frame = JFrame("FrameDemo")
|
||||
frame.setSize(175, 100)
|
||||
|
||||
frame.setDefaultCloseOperation(WindowConstants.<caret>DISPOSE_ON_CLOSE)
|
||||
frame.isVisible = true
|
||||
}""".trimIndent())
|
||||
}
|
||||
+40
@@ -0,0 +1,40 @@
|
||||
// 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.kotlin.training.ift.lesson.completion
|
||||
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import training.dsl.LearningDslBase
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.parseLessonSample
|
||||
import training.learn.lesson.general.completion.PostfixCompletionLesson
|
||||
|
||||
class KotlinPostfixCompletionLesson : PostfixCompletionLesson() {
|
||||
override val sample: LessonSample = parseLessonSample(
|
||||
"""
|
||||
fun demonstrate(showTimes: Int) {
|
||||
showTimes<caret>
|
||||
}
|
||||
""".trimIndent()
|
||||
)
|
||||
|
||||
override val result: String = parseLessonSample(
|
||||
"""
|
||||
fun demonstrate(showTimes: Int) {
|
||||
for (i in 0 until showTimes) {
|
||||
|
||||
}
|
||||
}
|
||||
""".trimIndent()
|
||||
).text
|
||||
|
||||
|
||||
override val completionSuffix: String = ".fo"
|
||||
override val completionItem: String = "fori"
|
||||
|
||||
override fun LearningDslBase.getTypeTaskText(): String {
|
||||
return JavaLessonsBundle.message("java.postfix.completion.type", code(completionSuffix))
|
||||
}
|
||||
|
||||
override fun LearningDslBase.getCompleteTaskText(): String {
|
||||
return JavaLessonsBundle.message("java.postfix.completion.complete", code(completionItem), action("EditorChooseLookupItem"))
|
||||
}
|
||||
}
|
||||
+33
@@ -0,0 +1,33 @@
|
||||
// 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.kotlin.training.ift.lesson.completion
|
||||
|
||||
import com.intellij.java.ift.lesson.completion.SmartTypeCompletionLessonBase
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.parseLessonSample
|
||||
|
||||
class KotlinSmartTypeCompletionLesson : SmartTypeCompletionLessonBase() {
|
||||
|
||||
override val sample: LessonSample = parseLessonSample("""
|
||||
object TestObject
|
||||
|
||||
fun smartCompletionDemo(): TestObject {
|
||||
val iterations = 5
|
||||
repeat(<caret>) {
|
||||
println("Hello")
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
override val firstCompletionItem: String = "iterations"
|
||||
override val firstCompletionCheck: String = "repeat(iterations)"
|
||||
|
||||
override val secondCompletionItem: String = "TestObject"
|
||||
override val secondCompletionCheck: String = "return TestObject"
|
||||
|
||||
override fun LessonContext.setCaretForSecondItem() {
|
||||
caret(9, 12)
|
||||
}
|
||||
}
|
||||
+92
@@ -0,0 +1,92 @@
|
||||
// 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.kotlin.training.ift.lesson.essential
|
||||
|
||||
|
||||
import com.intellij.java.ift.lesson.essential.OnboardingTourLessonBase
|
||||
import com.intellij.openapi.ui.popup.Balloon
|
||||
import org.jetbrains.annotations.Nls
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.training.ift.KotlinLessonsBundle
|
||||
import training.dsl.*
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.util.*
|
||||
|
||||
class KotlinOnboardingTourLesson : OnboardingTourLessonBase("kotlin.onboarding") {
|
||||
override val demoFileExtension: String = "kt"
|
||||
override val learningProjectName: String = "KotlinLearningProject"
|
||||
private val samplePrintln = "println(\"AVERAGE of array \" + array.joinToString() + \" is \" + findAverage(array))"
|
||||
override val sample: LessonSample = parseLessonSample("""
|
||||
fun findAverage(values: IntArray): Double {
|
||||
var result = 0.0
|
||||
for (i in 0 un<caret id=3/>til values.size) {
|
||||
result += values[i]
|
||||
}
|
||||
<caret>return result<caret id=2/>
|
||||
}
|
||||
|
||||
fun main() {
|
||||
val array = intArrayOf(5, 6, 7, 8)
|
||||
$samplePrintln
|
||||
}
|
||||
""".trimIndent())
|
||||
|
||||
override val completionStepExpectedCompletion: String = "size"
|
||||
|
||||
override fun LessonContext.contextActions() {
|
||||
val quickFixMessage = KotlinBundle.message("replace.index.loop.with.collection.loop.quick.fix.text")
|
||||
caret(sample.getPosition(3))
|
||||
|
||||
task {
|
||||
triggerOnEditorText("until", highlightBorder = true)
|
||||
}
|
||||
|
||||
task("ShowIntentionActions") {
|
||||
text(KotlinLessonsBundle.message("kotlin.onboarding.invoke.intention.for.warning.1"))
|
||||
text(KotlinLessonsBundle.message("kotlin.onboarding.invoke.intention.for.warning.2", action(it)))
|
||||
text(KotlinLessonsBundle.message("kotlin.onboarding.invoke.intention.for.warning.balloon", action(it)),
|
||||
LearningBalloonConfig(Balloon.Position.above, width = 0, cornerToPointerDistance = 80))
|
||||
triggerAndBorderHighlight().listItem { item ->
|
||||
item.isToStringContains(quickFixMessage)
|
||||
}
|
||||
restoreIfModifiedOrMoved()
|
||||
}
|
||||
|
||||
task {
|
||||
text(KotlinLessonsBundle.message("kotlin.onboarding.select.fix", strong(quickFixMessage)))
|
||||
stateCheck {
|
||||
editor.document.text.contains("for (element in values)")
|
||||
}
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
}
|
||||
|
||||
fun getIntentionMessage(): @Nls String {
|
||||
return KotlinBundle.message("convert.concatenation.to.template")
|
||||
}
|
||||
|
||||
caret("RAGE")
|
||||
|
||||
task {
|
||||
triggerOnEditorText("AVERAGE")
|
||||
}
|
||||
|
||||
task("ShowIntentionActions") {
|
||||
text(KotlinLessonsBundle.message("kotlin.onboarding.invoke.intention.for.code", action(it)))
|
||||
text(KotlinLessonsBundle.message("kotlin.onboarding.invoke.intention.for.code.balloon", action(it)),
|
||||
LearningBalloonConfig(Balloon.Position.below, width = 0))
|
||||
val intentionMessage = getIntentionMessage()
|
||||
triggerAndBorderHighlight().listItem { item ->
|
||||
item.isToStringContains(intentionMessage)
|
||||
}
|
||||
restoreIfModifiedOrMoved()
|
||||
}
|
||||
|
||||
task {
|
||||
text(KotlinLessonsBundle.message("kotlin.onboarding.apply.intention", strong(getIntentionMessage()), LessonUtil.rawEnter()))
|
||||
stateCheck {
|
||||
val text = editor.document.text
|
||||
text.contains("\${array.joinToString()}")
|
||||
}
|
||||
restoreByUi(delayMillis = defaultRestoreDelay)
|
||||
}
|
||||
}
|
||||
}
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
// 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.kotlin.training.ift.lesson.navigation
|
||||
|
||||
import com.intellij.psi.PsiElement
|
||||
import com.intellij.psi.util.parentOfType
|
||||
import org.jetbrains.kotlin.psi.KtCallExpression
|
||||
import training.dsl.LessonContext
|
||||
import training.learn.lesson.general.navigation.DeclarationAndUsagesLesson
|
||||
|
||||
class KotlinDeclarationAndUsagesLesson : DeclarationAndUsagesLesson() {
|
||||
override fun LessonContext.setInitialPosition(): Unit = caret("foo()")
|
||||
override val sampleFilePath: String get() = "src/DerivedClass2.kt"
|
||||
override val entityName: String = "foo"
|
||||
|
||||
override fun getParentExpression(element: PsiElement): PsiElement? {
|
||||
return element.takeIf { element.parentOfType<KtCallExpression>() != null }
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
// 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.kotlin.training.ift.lesson.navigation
|
||||
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonUtil
|
||||
import training.learn.LessonsBundle
|
||||
import training.learn.lesson.general.navigation.RecentFilesLesson
|
||||
|
||||
class KotlinRecentFilesLesson : RecentFilesLesson() {
|
||||
override val sampleFilePath: String = "src/RecentFilesDemo.kt"
|
||||
|
||||
override val transitionMethodName: String = "println"
|
||||
override val transitionFileName: String = "Console"
|
||||
override val stringForRecentFilesSearch: String = "print"
|
||||
|
||||
override fun LessonContext.setInitialPosition(): Unit = caret("println")
|
||||
|
||||
override val helpLinks: Map<String, String>
|
||||
get() = mapOf(
|
||||
Pair(
|
||||
LessonsBundle.message("recent.files.locations.help.link"),
|
||||
LessonUtil.getHelpLink("idea", "discover-intellij-idea.html#recent-files")
|
||||
),
|
||||
)
|
||||
}
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
// 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.kotlin.training.ift.lesson.refactorings
|
||||
|
||||
import com.intellij.java.ift.JavaLessonsBundle
|
||||
import org.jetbrains.kotlin.idea.base.resources.KotlinBundle
|
||||
import org.jetbrains.kotlin.training.ift.KotlinLessonsBundle
|
||||
import training.dsl.LessonContext
|
||||
import training.dsl.LessonSample
|
||||
import training.dsl.LessonUtil
|
||||
import training.dsl.LessonUtil.restoreIfModifiedOrMoved
|
||||
import training.dsl.dropMnemonic
|
||||
import training.dsl.parseLessonSample
|
||||
import training.dsl.restoreRefactoringOptionsInformer
|
||||
import training.learn.lesson.general.refactorings.RefactoringMenuLessonBase
|
||||
import training.util.adaptToNotNativeLocalization
|
||||
|
||||
class KotlinRefactoringMenuLesson : RefactoringMenuLessonBase("java.refactoring.menu") {
|
||||
override val sample: LessonSample = parseLessonSample(
|
||||
"""
|
||||
import java.io.FileReader
|
||||
import java.io.BufferedReader
|
||||
|
||||
fun main() {
|
||||
val list = readStrings()
|
||||
val filtered = list.filter { it.isNotEmpty() }
|
||||
filtered.forEach {
|
||||
println(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun readStrings(): List<String> {
|
||||
return BufferedReader(<select>FileReader("input.txt")</select>).readLines()
|
||||
}""".trimIndent())
|
||||
|
||||
override val lessonContent: LessonContext.() -> Unit = {
|
||||
extractParameterTasks()
|
||||
moreRefactoringsTasks()
|
||||
restoreRefactoringOptionsInformer()
|
||||
}
|
||||
|
||||
private fun LessonContext.moreRefactoringsTasks() {
|
||||
waitBeforeContinue(300)
|
||||
|
||||
val inlineVariableName = "list"
|
||||
|
||||
caret(inlineVariableName)
|
||||
|
||||
actionTask("Inline") {
|
||||
restoreIfModifiedOrMoved()
|
||||
if (adaptToNotNativeLocalization) {
|
||||
JavaLessonsBundle.message(
|
||||
"java.refactoring.menu.inline.variable", code(inlineVariableName),
|
||||
action("Refactorings.QuickListPopupAction"), strong(KotlinBundle.message("title.inline.property")),
|
||||
action(it)
|
||||
)
|
||||
} else KotlinLessonsBundle.message(
|
||||
"kotlin.refactoring.menu.inline.property.eng",
|
||||
code(inlineVariableName), action("Refactorings.QuickListPopupAction"), action(it)
|
||||
)
|
||||
}
|
||||
task {
|
||||
stateCheck {
|
||||
!editor.document.charsSequence.contains(inlineVariableName)
|
||||
}
|
||||
}
|
||||
|
||||
caret("txt", true)
|
||||
|
||||
actionTask("IntroduceConstant") {
|
||||
restoreIfModifiedOrMoved()
|
||||
if (adaptToNotNativeLocalization) {
|
||||
JavaLessonsBundle.message(
|
||||
"java.refactoring.menu.introduce.constant", action("Refactorings.QuickListPopupAction"),
|
||||
strong(KotlinBundle.message("introduce.constant").dropMnemonic()), action(it)
|
||||
)
|
||||
} else JavaLessonsBundle.message(
|
||||
"java.refactoring.menu.introduce.constant.eng",
|
||||
action("Refactorings.QuickListPopupAction"), action(it)
|
||||
)
|
||||
}
|
||||
|
||||
actionTask("NextTemplateVariable") {
|
||||
KotlinLessonsBundle.message("kotlin.refactoring.menu.confirm.constant", LessonUtil.rawEnter())
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user