diff --git a/.idea/kotlinc.xml b/.idea/kotlinc.xml
index b1077fb..6d0ee1c 100644
--- a/.idea/kotlinc.xml
+++ b/.idea/kotlinc.xml
@@ -1,6 +1,6 @@
-
+
\ No newline at end of file
diff --git a/src/main/kotlin/com/github/takemikami/intellij/plugin/pytestparametrize/GoToParametrizeArgsAction.kt b/src/main/kotlin/com/github/takemikami/intellij/plugin/pytestparametrize/GoToParametrizeArgsAction.kt
new file mode 100644
index 0000000..5f1c6ef
--- /dev/null
+++ b/src/main/kotlin/com/github/takemikami/intellij/plugin/pytestparametrize/GoToParametrizeArgsAction.kt
@@ -0,0 +1,70 @@
+package com.github.takemikami.intellij.plugin.pytestparametrize
+
+
+import com.intellij.openapi.actionSystem.*
+import com.intellij.openapi.editor.CaretModel
+import com.intellij.openapi.editor.ScrollType
+import com.intellij.psi.PsiElement
+import com.intellij.psi.PsiRecursiveElementVisitor
+import com.intellij.refactoring.suggested.endOffset
+import com.intellij.refactoring.suggested.startOffset
+import com.jetbrains.python.psi.*
+
+
+class GoToParametrizeArgsAction : AnAction() {
+ override fun update(event: AnActionEvent) {
+ val vf = event.getData(CommonDataKeys.VIRTUAL_FILE) ?: return
+ val active = "py".equals(vf.extension) && vf.name.startsWith("test")
+ event.presentation.isEnabledAndVisible = active
+ }
+
+ class TestVisitor() : PsiRecursiveElementVisitor() {
+ override fun visitElement(element: PsiElement) {
+ if (element is PyDecorator
+ && "pytest.mark.parametrize".equals(element.qualifiedName.toString())
+ && element.hasArgumentList()
+ && element.arguments.size >= 2
+ ) {
+ val valList = element.arguments[1]
+ if (valList !is PyListLiteralExpression) return
+
+ // detect selected id
+ val idsArguments = element.arguments.filter { it.name.equals("ids") }
+ if (idsArguments.isEmpty()) return
+
+ val targetIndex = idsArguments.first().children.first().children.map {
+ currentOffset >= it.startOffset && currentOffset <= it.endOffset
+ }.indexOfFirst { it }
+ if (targetIndex == -1) return
+
+ // detect args offset
+ val argsOffsets = valList.elements.map { it.startOffset }
+ if (targetIndex + 1 > argsOffsets.size) return
+ offset = argsOffsets[targetIndex]
+ }
+ super.visitElement(element)
+ }
+
+ var offset = -1
+ var currentOffset = -1
+ }
+
+ override fun actionPerformed(event: AnActionEvent) {
+ val editor = event.getData(CommonDataKeys.EDITOR)
+ val caretModel: CaretModel = editor?.caretModel ?: return
+ val logicalPosition = caretModel.logicalPosition
+
+ // get offset to goto
+ val psiFile = event.getData(PlatformDataKeys.PSI_FILE)
+ val visitor: TestVisitor = TestVisitor()
+ visitor.currentOffset = caretModel.offset
+ psiFile?.accept(visitor)
+ if (visitor.offset == -1) return
+
+ // move caret
+ logicalPosition.leanForward(true);
+ caretModel.moveToOffset(visitor.offset)
+ editor.scrollingModel.scrollToCaret(ScrollType.CENTER_DOWN)
+ editor.selectionModel.removeSelection()
+ }
+}
diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml
index 028f0ee..cc855c6 100644
--- a/src/main/resources/META-INF/plugin.xml
+++ b/src/main/resources/META-INF/plugin.xml
@@ -31,4 +31,10 @@
+
+
+
+
+
+