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
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
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
28 override val lessonContent: LessonContext.() -> Unit
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)
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) {
47 val navigationElement = before.target.navigationElement
48 return@l navigationElement == now.target.navigationElement &&
49 isInsidePsi(navigationElement, before.position) &&
50 !isInsidePsi(navigationElement, now.position)
55 waitComponent(JBTable::class.java, "ShowUsagesTable")
62 text(LessonsBundle.message("declaration.and.usages.find.usages", action(it)))
64 triggerByUiComponentAndHighlight { ui: BaseLabel ->
65 ui.text?.contains(LearnBundle.message("usages.tab.name")) ?: false
72 val pinTabText = UIBundle.message("tabbed.pane.pin.tab.action.name")
76 previous.ui?.let { usagesTab -> jComponent(usagesTab).rightClick() }
79 triggerByUiComponentAndHighlight(highlightInside = false) { ui: ActionMenuItem ->
80 ui.text?.contains(pinTabText) ?: false
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"))))
87 task("PinToolwindowTab") {
90 text(LessonsBundle.message("declaration.and.usages.select.pin.item", strong(pinTabText)))
93 jComponent(previous.ui!!).click()
98 actionTask("HideActiveWindow") {
99 LessonsBundle.message("declaration.and.usages.hide.view", action(it))
102 actionTask("ActivateFindToolWindow") {
103 LessonsBundle.message("declaration.and.usages.open.find.view",
104 action(it), strong(UIBundle.message("tool.window.name.find")))
108 private fun TaskRuntimeContext.state(): MyInfo? {
109 val flags = TargetElementUtil.ELEMENT_NAME_ACCEPTED or TargetElementUtil.REFERENCED_ELEMENT_ACCEPTED
111 val currentEditor = FileEditorManager.getInstance(project).selectedTextEditor ?: return null
113 val target = TargetElementUtil.findTargetElement(currentEditor, flags) ?: return null
115 val file = PsiDocumentManager.getInstance(project).getPsiFile(currentEditor.document) ?: return null
116 val position = MyPosition(file,
117 currentEditor.caretModel.offset)
119 return MyInfo(target, position)
122 private fun isInsidePsi(psi: PsiElement, position: MyPosition): Boolean {
123 return psi.containingFile == position.file && psi.textRange.contains(position.offset)
126 private data class MyInfo(val target: PsiElement, val position: MyPosition)
128 private data class MyPosition(val file: PsiFile, val offset: Int)