[IFT] UI detection fix for i18n case
[idea/contrib.git] / ide-features-trainer / src / training / learn / lesson / general / navigation / DeclarationAndUsagesLesson.kt
1 // Copyright 2000-2020 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.
2 package training.learn.lesson.general.navigation
3
4 import com.intellij.codeInsight.TargetElementUtil
5 import com.intellij.openapi.actionSystem.impl.ActionMenuItem
6 import com.intellij.openapi.fileEditor.FileEditorManager
7 import com.intellij.openapi.wm.impl.content.BaseLabel
8 import com.intellij.psi.PsiDocumentManager
9 import com.intellij.psi.PsiElement
10 import com.intellij.psi.PsiFile
11 import com.intellij.testGuiFramework.framework.GuiTestUtil.shortcut
12 import com.intellij.testGuiFramework.util.Key
13 import com.intellij.ui.UIBundle
14 import com.intellij.ui.table.JBTable
15 import training.commands.kotlin.TaskRuntimeContext
16 import training.learn.LearnBundle
17 import training.learn.LessonsBundle
18 import training.learn.interfaces.Module
19 import training.learn.lesson.kimpl.KLesson
20 import training.learn.lesson.kimpl.LessonContext
21
22 abstract class DeclarationAndUsagesLesson(module: Module, lang: String)
23   : KLesson("Declaration and usages", LessonsBundle.message("declaration.and.usages.lesson.name"), module, lang) {
24   abstract fun LessonContext.setInitialPosition()
25   abstract val typeOfEntity: Int // 0 - method, 1 - attribute accessor
26   abstract override val existedFile: String
27
28   override val lessonContent: LessonContext.() -> Unit
29     get() = {
30       setInitialPosition()
31
32       task("GotoDeclaration") {
33         text(LessonsBundle.message("declaration.and.usages.jump.to.declaration", action(it), typeOfEntity))
34         trigger(it, { state() }) { before, _ ->
35           before != null && !isInsidePsi(before.target.navigationElement, before.position)
36         }
37         test { actions(it) }
38       }
39
40       task("GotoDeclaration") {
41         text(LessonsBundle.message("declaration.and.usages.show.usages", typeOfEntity, action(it)))
42         trigger(it, { state() }) l@{ before, now ->
43           if (before == null || now == null) {
44             return@l false
45           }
46
47           val navigationElement = before.target.navigationElement
48           return@l navigationElement == now.target.navigationElement &&
49                    isInsidePsi(navigationElement, before.position) &&
50                    !isInsidePsi(navigationElement, now.position)
51         }
52         test {
53           actions(it)
54           ideFrame {
55             waitComponent(JBTable::class.java, "ShowUsagesTable")
56             shortcut(Key.ENTER)
57           }
58         }
59       }
60
61       task("FindUsages") {
62         text(LessonsBundle.message("declaration.and.usages.find.usages", action(it)))
63
64         triggerByUiComponentAndHighlight { ui: BaseLabel ->
65           ui.text?.contains(LearnBundle.message("usages.tab.name")) ?: false
66         }
67         test {
68           actions(it)
69         }
70       }
71
72       val pinTabText = UIBundle.message("tabbed.pane.pin.tab.action.name")
73       task {
74         test {
75           ideFrame {
76             previous.ui?.let { usagesTab -> jComponent(usagesTab).rightClick() }
77           }
78         }
79         triggerByUiComponentAndHighlight(highlightInside = false) { ui: ActionMenuItem ->
80           ui.text?.contains(pinTabText) ?: false
81         }
82         restoreByUi()
83         text(LessonsBundle.message("declaration.and.usages.pin.motivation", strong(UIBundle.message("tool.window.name.find"))))
84         text(LessonsBundle.message("declaration.and.usages.right.click.tab", strong(LearnBundle.message("usages.tab.name"))))
85       }
86
87       task("PinToolwindowTab") {
88         trigger(it)
89         restoreByUi()
90         text(LessonsBundle.message("declaration.and.usages.select.pin.item", strong(pinTabText)))
91         test {
92           ideFrame {
93             jComponent(previous.ui!!).click()
94           }
95         }
96       }
97
98       actionTask("HideActiveWindow") {
99         LessonsBundle.message("declaration.and.usages.hide.view", action(it))
100       }
101
102       actionTask("ActivateFindToolWindow") {
103         LessonsBundle.message("declaration.and.usages.open.find.view",
104                               action(it), strong(UIBundle.message("tool.window.name.find")))
105       }
106     }
107
108   private fun TaskRuntimeContext.state(): MyInfo? {
109     val flags = TargetElementUtil.ELEMENT_NAME_ACCEPTED or TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED
110
111     val currentEditor = FileEditorManager.getInstance(project).selectedTextEditor ?: return null
112
113     val target = TargetElementUtil.findTargetElement(currentEditor, flags) ?: return null
114
115     val file = PsiDocumentManager.getInstance(project).getPsiFile(currentEditor.document) ?: return null
116     val position = MyPosition(file,
117                               currentEditor.caretModel.offset)
118
119     return MyInfo(target, position)
120   }
121
122   private fun isInsidePsi(psi: PsiElement, position: MyPosition): Boolean {
123     return psi.containingFile == position.file && psi.textRange.contains(position.offset)
124   }
125
126   private data class MyInfo(val target: PsiElement, val position: MyPosition)
127
128   private data class MyPosition(val file: PsiFile, val offset: Int)
129 }