From 39768758536e58712fa4096707c3461548cc319e Mon Sep 17 00:00:00 2001 From: Pavel Shurmilov Date: Mon, 1 Nov 2021 17:10:44 +0300 Subject: [PATCH 1/2] Add navigation to twine labels from XML files --- build.gradle | 4 +- src/main/kotlin/by/overpass/twap/Twine.kt | 1 + .../twap/lang/TwineLineMarkerProvider.kt | 45 --------------- .../JavaTwineLabelLineMarkerProvider.kt | 29 ++++++++++ .../TwineLabelLineMarkerProvider.kt | 44 ++++++++++++++ .../XmlTwineLabelLineMarkerProvider.kt | 26 +++++++++ src/main/resources/META-INF/plugin.xml | 4 +- .../JavaTwineLabelLineMarkerProviderTest.kt | 46 +++++++++++++++ .../XmlTwineLabelLineMarkerProviderTest.kt | 57 +++++++++++++++++++ 9 files changed, 208 insertions(+), 48 deletions(-) delete mode 100644 src/main/kotlin/by/overpass/twap/lang/TwineLineMarkerProvider.kt create mode 100644 src/main/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProvider.kt create mode 100644 src/main/kotlin/by/overpass/twap/lang/navigation/TwineLabelLineMarkerProvider.kt create mode 100644 src/main/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProvider.kt create mode 100644 src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt create mode 100644 src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt diff --git a/build.gradle b/build.gradle index fa2deff..f6f6a9e 100644 --- a/build.gradle +++ b/build.gradle @@ -45,8 +45,8 @@ intellij { runIde { if (Os.isFamily(Os.FAMILY_MAC)) { -// // to run with the real Android Studio -// ideDirectory = '/Applications/Android Studio.app/Contents' + // to run with the real Android Studio +// ideDir.set(file("/Applications/Android Studio.app/Contents")) } } diff --git a/src/main/kotlin/by/overpass/twap/Twine.kt b/src/main/kotlin/by/overpass/twap/Twine.kt index 4e38cc5..7af37a1 100644 --- a/src/main/kotlin/by/overpass/twap/Twine.kt +++ b/src/main/kotlin/by/overpass/twap/Twine.kt @@ -10,5 +10,6 @@ object Twine { const val EXT = "twine" val icon = IconLoader.getIcon("/icons/ic_twine.png", Twine.javaClass) val androidStringResourceRegex = "R\\.string\\.(.+)".toRegex() + val androidStringXmlReferenceRegex = "@string/(.+)".toRegex() val twineIdentifierRegex = "[a-zA-Z0-9_]+(\\.[a-zA-Z0-9_]+)*".toRegex() } \ No newline at end of file diff --git a/src/main/kotlin/by/overpass/twap/lang/TwineLineMarkerProvider.kt b/src/main/kotlin/by/overpass/twap/lang/TwineLineMarkerProvider.kt deleted file mode 100644 index 20fe63f..0000000 --- a/src/main/kotlin/by/overpass/twap/lang/TwineLineMarkerProvider.kt +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Line marker provider navigation - */ - -package by.overpass.twap.lang - -import by.overpass.twap.Twine -import by.overpass.twap.lang.reference.identifier.underscoreReplacement -import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo -import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider -import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder -import com.intellij.psi.PsiElement -import com.intellij.psi.PsiIdentifier -import com.intellij.psi.PsiReferenceExpression -import com.intellij.util.castSafelyTo - -typealias NavMarkersResultCollection = MutableCollection?> - -/** - * Provides line marker navigation to corresponding [by.overpass.twap.lang.parsing.psi.TwineIdentifier]s - */ -class TwineLineMarkerProvider : RelatedItemLineMarkerProvider() { - - override fun collectNavigationMarkers( - element: PsiElement, - result: NavMarkersResultCollection, - ) { - element.takeIf { it is PsiIdentifier } - ?.castSafelyTo() - ?.takeIf { it.parent is PsiReferenceExpression } - ?.parent - ?.let { Twine.androidStringResourceRegex.find(it.text) } - ?.groupValues - ?.get(1) - ?.run(underscoreReplacement) - ?.let { element.project.findTwineIds(it) } - ?.takeIf { it.isNotEmpty() } - ?.let { - val builder = NavigationGutterIconBuilder.create(Twine.icon) - .setTargets(it) - .setTooltipText("Navigate to Twine label") - result += builder.createLineMarkerInfo(element) - } - } -} \ No newline at end of file diff --git a/src/main/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProvider.kt b/src/main/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProvider.kt new file mode 100644 index 0000000..b7d1b2c --- /dev/null +++ b/src/main/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProvider.kt @@ -0,0 +1,29 @@ +package by.overpass.twap.lang.navigation + +import by.overpass.twap.Twine +import by.overpass.twap.lang.reference.identifier.underscoreReplacement +import com.intellij.psi.PsiElement +import com.intellij.psi.PsiIdentifier +import com.intellij.psi.PsiReferenceExpression +import com.intellij.util.castSafelyTo + +/** + * Provides line marker navigation to corresponding [by.overpass.twap.lang.parsing.psi.TwineIdentifier]s from Java + */ +class JavaTwineLabelLineMarkerProvider : TwineLabelLineMarkerProvider() { + + override fun collectNavigationMarkers( + element: PsiElement, + result: NavMarkersResultCollection, + ) { + element.takeIf { it is PsiIdentifier } + ?.castSafelyTo() + ?.takeIf { it.parent is PsiReferenceExpression } + ?.parent + ?.let { Twine.androidStringResourceRegex.find(it.text) } + ?.groupValues + ?.get(1) + ?.run(underscoreReplacement) + ?.let { collectTwineIdRefs(element, result, it) } + } +} \ No newline at end of file diff --git a/src/main/kotlin/by/overpass/twap/lang/navigation/TwineLabelLineMarkerProvider.kt b/src/main/kotlin/by/overpass/twap/lang/navigation/TwineLabelLineMarkerProvider.kt new file mode 100644 index 0000000..7897f39 --- /dev/null +++ b/src/main/kotlin/by/overpass/twap/lang/navigation/TwineLabelLineMarkerProvider.kt @@ -0,0 +1,44 @@ +/** + * Line Marker Provider for navigation to Twine labels + */ + +package by.overpass.twap.lang.navigation + +import by.overpass.twap.Twine +import by.overpass.twap.lang.findTwineIds +import com.intellij.codeInsight.daemon.RelatedItemLineMarkerInfo +import com.intellij.codeInsight.daemon.RelatedItemLineMarkerProvider +import com.intellij.codeInsight.navigation.NavigationGutterIconBuilder +import com.intellij.psi.PsiElement + +typealias NavMarkersResultCollection = MutableCollection?> + +/** + * Provides line marker navigation to corresponding [by.overpass.twap.lang.parsing.psi.TwineIdentifier]s + */ +open class TwineLabelLineMarkerProvider : RelatedItemLineMarkerProvider() { + /** + * @param element currently processed [PsiElement] + * @param result result nav markers collection + * @param id sought twine label id + */ + protected fun collectTwineIdRefs( + element: PsiElement, + result: NavMarkersResultCollection, + id: String, + ) { + element.project + .findTwineIds(id) + .takeIf { it.isNotEmpty() } + ?.let { + val builder = NavigationGutterIconBuilder.create(Twine.icon) + .setTargets(it) + .setTooltipText(MESSAGE_NAVIGATE_TO_TWINE_LABEL) + result += builder.createLineMarkerInfo(element) + } + } + + companion object { + const val MESSAGE_NAVIGATE_TO_TWINE_LABEL = "Navigate to Twine label" + } +} \ No newline at end of file diff --git a/src/main/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProvider.kt b/src/main/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProvider.kt new file mode 100644 index 0000000..b67f246 --- /dev/null +++ b/src/main/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProvider.kt @@ -0,0 +1,26 @@ +package by.overpass.twap.lang.navigation + +import by.overpass.twap.Twine +import com.intellij.psi.PsiElement +import com.intellij.psi.xml.XmlToken +import com.intellij.psi.xml.XmlTokenType +import com.intellij.util.castSafelyTo + +/** + * Provides line marker navigation to corresponding [by.overpass.twap.lang.parsing.psi.TwineIdentifier]s from XML + */ +class XmlTwineLabelLineMarkerProvider : TwineLabelLineMarkerProvider() { + + override fun collectNavigationMarkers( + element: PsiElement, + result: NavMarkersResultCollection, + ) { + element.takeIf { it is XmlToken } + ?.castSafelyTo() + ?.takeIf { it.tokenType == XmlTokenType.XML_ATTRIBUTE_VALUE_TOKEN } + ?.let { Twine.androidStringXmlReferenceRegex.find(it.text) } + ?.groupValues + ?.get(1) + ?.let { collectTwineIdRefs(element, result, it) } + } +} \ No newline at end of file diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index eea690a..cf489c9 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -31,7 +31,9 @@ + implementationClass="by.overpass.twap.lang.navigation.JavaTwineLabelLineMarkerProvider"/> + diff --git a/src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt b/src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt new file mode 100644 index 0000000..3c4019b --- /dev/null +++ b/src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt @@ -0,0 +1,46 @@ +package by.overpass.twap.lang.navigation + +import by.overpass.twap.Twine +import by.overpass.twap.lang.TwineFileType +import com.intellij.ide.highlighter.JavaFileType +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase +import org.junit.Test + +/** + * Test for [JavaTwineLabelLineMarkerProvider] + */ +class JavaTwineLabelLineMarkerProviderTest : JavaCodeInsightFixtureTestCase() { + + @Test + fun testGutterIconIsDisplayed() = with(myFixture) { + configureByText( + TwineFileType, + """ + [[section]] + [label1] + en = en + ru = ru + """.trimIndent() + ) + configureByText( + JavaFileType.INSTANCE, + """ + package com.example; + + public class Main { + + public static void main(String[] args) { + int a = R.string.label1; + System.out.println(a); + } + } + """.trimIndent() + ) + + val gutterIcons = findGuttersAtCaret() + assertEquals(1, gutterIcons.size) + val twineGutterIcon = gutterIcons.first() + assertEquals(Twine.icon, twineGutterIcon.icon) + assertEquals(TwineLabelLineMarkerProvider.MESSAGE_NAVIGATE_TO_TWINE_LABEL, twineGutterIcon.tooltipText) + } +} \ No newline at end of file diff --git a/src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt b/src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt new file mode 100644 index 0000000..a29df60 --- /dev/null +++ b/src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt @@ -0,0 +1,57 @@ +package by.overpass.twap.lang.navigation + +import by.overpass.twap.Twine +import by.overpass.twap.lang.TwineFileType +import com.intellij.ide.highlighter.XmlFileType +import com.intellij.testFramework.fixtures.JavaCodeInsightFixtureTestCase +import org.junit.Test + +/** + * Test for [XmlTwineLabelLineMarkerProvider] + */ +class XmlTwineLabelLineMarkerProviderTest : JavaCodeInsightFixtureTestCase() { + + private val testXmlFileContent = """ + + + + + + + """.trimIndent() + + @Test + fun testGutterIconIsVisible() = with(myFixture) { + configureByText( + TwineFileType, + """ + [[section]] + [label1.lab1] + en = en + ru = ru + """.trimIndent() + ) + configureByText( + XmlFileType.INSTANCE, + testXmlFileContent + ) + + val gutterIcons = findGuttersAtCaret() + assertEquals(1, gutterIcons.size) + val twineGutterIcon = gutterIcons.first() + assertEquals(Twine.icon, twineGutterIcon.icon) + assertEquals(TwineLabelLineMarkerProvider.MESSAGE_NAVIGATE_TO_TWINE_LABEL, twineGutterIcon.tooltipText) + } +} \ No newline at end of file From 8186ea52e90a238a85dd452d1813e1bf2867aafe Mon Sep 17 00:00:00 2001 From: Pavel Shurmilov Date: Mon, 1 Nov 2021 17:18:06 +0300 Subject: [PATCH 2/2] Add comments to test functions --- .../lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt | 3 +++ .../lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt | 3 +++ 2 files changed, 6 insertions(+) diff --git a/src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt b/src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt index 3c4019b..4522a9b 100644 --- a/src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt +++ b/src/test/kotlin/by/overpass/twap/lang/navigation/JavaTwineLabelLineMarkerProviderTest.kt @@ -11,6 +11,9 @@ import org.junit.Test */ class JavaTwineLabelLineMarkerProviderTest : JavaCodeInsightFixtureTestCase() { + /** + * Test that the twine gutter icon is present + */ @Test fun testGutterIconIsDisplayed() = with(myFixture) { configureByText( diff --git a/src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt b/src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt index a29df60..b32bd08 100644 --- a/src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt +++ b/src/test/kotlin/by/overpass/twap/lang/navigation/XmlTwineLabelLineMarkerProviderTest.kt @@ -32,6 +32,9 @@ class XmlTwineLabelLineMarkerProviderTest : JavaCodeInsightFixtureTestCase() { """.trimIndent() + /** + * Test that the twine gutter icon is present + */ @Test fun testGutterIconIsVisible() = with(myFixture) { configureByText(