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