diff --git a/.idea/gradle.xml b/.idea/gradle.xml deleted file mode 100644 index e16b16d..0000000 --- a/.idea/gradle.xml +++ /dev/null @@ -1,17 +0,0 @@ - - - - - - - diff --git a/build.gradle.kts b/build.gradle.kts index aabb8cc..a2e6efe 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,6 +1,5 @@ import org.jetbrains.changelog.Changelog import org.jetbrains.changelog.markdownToHTML -import org.jetbrains.kotlin.gradle.tasks.KotlinCompile fun properties(key: String) = providers.gradleProperty(key) fun environment(key: String) = providers.environmentVariable(key) @@ -9,11 +8,11 @@ plugins { // Java support id("java") // Kotlin support - id("org.jetbrains.kotlin.jvm") version "1.8.10" + id("org.jetbrains.kotlin.jvm") version "1.9.25" // Gradle IntelliJ Plugin - id("org.jetbrains.intellij") version "1.13.2" + id("org.jetbrains.intellij") version "1.17.2" // Gradle Changelog Plugin - id("org.jetbrains.changelog") version "2.0.0" + id("org.jetbrains.changelog") version "2.2.1" // Gradle Qodana Plugin id("org.jetbrains.qodana") version "0.1.13" // Gradle Kover Plugin @@ -30,7 +29,7 @@ repositories { // Set the JVM language level used to build the project. Use Java 11 for 2020.3+, and Java 17 for 2022.2+. kotlin { - jvmToolchain(11) + jvmToolchain(17) } // Configure Gradle IntelliJ Plugin - read more: https://plugins.jetbrains.com/docs/intellij/tools-gradle-intellij-plugin.html diff --git a/gradle.properties b/gradle.properties index 75e36d7..f5cd7d4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,7 +4,7 @@ pluginGroup = dev.encore.intellij pluginName = Encore # SemVer format -> https://semver.org -pluginVersion = 0.2.0 +pluginVersion = 0.3.0 # See https://plugins.jetbrains.com/docs/intellij/build-number-ranges.html # for insight into build numbers and IntelliJ Platform versions. @@ -21,7 +21,7 @@ platformDownloadSources = true platformPlugins = org.jetbrains.plugins.go, com.intellij.database # Gradle Releases -> https://github.com/gradle/gradle/releases -gradleVersion = 8.0.2 +gradleVersion = 8.12 # Opt-out flag for bundling Kotlin standard library. # See https://jb.gg/intellij-platform-kotlin-stdlib for details. diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index bdc9a83..18362b7 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.12-bin.zip networkTimeout=10000 zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/kotlin/dev/encore/intellij/annotators/ApiDecls.kt b/src/main/kotlin/dev/encore/intellij/annotators/ApiDecls.kt index 7e0da64..7adde2a 100644 --- a/src/main/kotlin/dev/encore/intellij/annotators/ApiDecls.kt +++ b/src/main/kotlin/dev/encore/intellij/annotators/ApiDecls.kt @@ -1,37 +1,37 @@ package dev.encore.intellij.annotators import com.goide.highlighting.GoSyntaxHighlightingColors +import com.goide.psi.GoFunctionDeclaration import com.intellij.codeInspection.ProblemHighlightType import com.intellij.lang.annotation.AnnotationHolder import com.intellij.lang.annotation.Annotator import com.intellij.lang.annotation.HighlightSeverity import com.intellij.openapi.editor.DefaultLanguageHighlighterColors -import com.intellij.openapi.editor.markup.GutterIconRenderer import com.intellij.openapi.util.TextRange import com.intellij.psi.PsiComment import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil class ApiDecls : Annotator { override fun annotate(element: PsiElement, holder: AnnotationHolder) { - // If it's not a comment and doesn't start with "//encore:", return - // as we're not interested in it - if (element !is PsiComment) { + // If it's not a comment, doesn't start with "//encore:" or is not + // followed by a func definition, return as we're not interested in it + if (element !is PsiComment || PsiTreeUtil.skipWhitespacesForward(element) !is GoFunctionDeclaration) { return } val comment: PsiComment = element - if (!comment.text.startsWith(API_DECL_PREFIX)) { - return - } - val parts = comment.text.removePrefix("//").split(" ") - if (parts.isEmpty() || !PREFIXES.containsKey(parts[0]) ) { + + // Parse the comment. Return if prefix is not known to us + val (prefix, cfg, args) = parseApiAnnotation(comment) + if (cfg == null) { return } - val cfg: DeclCfg = PREFIXES[parts[0]]!! + // Skip the initial "//" + val startOffset = comment.textRange.startOffset + 2 - - // Highlight the "//encore:" part of the comment as a comment keyword - val directiveRange = TextRange.from(comment.textRange.startOffset + 2, parts[0].length) + // Highlight the "encore:" part of the comment as a comment keyword + val directiveRange = TextRange.from(startOffset, prefix.length) holder.newSilentAnnotation(HighlightSeverity.INFORMATION) .range(directiveRange) .textAttributes(GoSyntaxHighlightingColors.COMMENT_KEYWORD) @@ -39,17 +39,17 @@ class ApiDecls : Annotator { .create() // Highlight the rest of the comment - var lastIndex = comment.textRange.startOffset + parts[0].length + 3 + var lastOffset = startOffset + prefix.length + 1 val used = mutableSetOf() - for (part in parts.drop(1)) { + for (part in args) { if (part.contains('=')) { // It's a field val splitPoint = part.indexOf('=') val fieldName = part.take(splitPoint) val fieldValue = part.drop(splitPoint + 1) - val nameRange = TextRange.from(lastIndex, splitPoint) - val valueRange = TextRange.from(lastIndex + splitPoint + 1, fieldValue.length) + val nameRange = TextRange.from(lastOffset, splitPoint) + val valueRange = TextRange.from(lastOffset + splitPoint + 1, fieldValue.length) // Check it's not already used once @@ -74,13 +74,13 @@ class ApiDecls : Annotator { .create() } - highlightFieldValue(holder, cfg, fieldName, fieldValue, valueRange) + highlightFieldValue(holder, fieldName, fieldValue, valueRange) used.add(fieldName) } else { // It's an option - val partRange = TextRange.from(lastIndex, part.length) + val partRange = TextRange.from(lastOffset, part.length) // Check it's not already used once if (used.contains(part)) { @@ -106,11 +106,11 @@ class ApiDecls : Annotator { used.add(part) } - lastIndex += part.length + 1 + lastOffset += part.length + 1 } } - private fun highlightFieldValue(holder: AnnotationHolder, cfg: DeclCfg, fieldName: String, fieldValue: String, range: TextRange) { + private fun highlightFieldValue(holder: AnnotationHolder, fieldName: String, fieldValue: String, range: TextRange) { if (fieldName == "path" && (fieldValue.contains(":") || fieldValue.contains("*"))) { // highlight each segment starting with ":" as a different colour var lastIndex = range.startOffset @@ -156,33 +156,44 @@ class ApiDecls : Annotator { .create() } } +} - companion object { - private const val API_DECL_PREFIX = "//encore:" - - private val PREFIXES = mapOf( - "encore:api" to DeclCfg( - "This defines an API endpoint", - arrayOf("raw", "public", "private", "auth"), - arrayOf("path", "method"), - ), - "encore:service" to DeclCfg( - "This defines a service singleton which will be started up along side your service", - arrayOf(), - arrayOf(), - ), - "encore:authhandler" to DeclCfg( - "This defines an authentication handler which will be used to authenticate requests for your whole application.", - arrayOf(), - arrayOf(), - ), - "encore:middleware" to DeclCfg( - "This defines a middleware which will be used to process requests", - arrayOf("global"), - arrayOf("target"), - ) - ) +private const val API_DECL_PREFIX = "//encore:" + +private val PREFIXES = mapOf( + "encore:api" to DeclCfg( + "This defines an API endpoint", + arrayOf("raw", "public", "private", "auth"), + arrayOf("path", "method"), + ), + "encore:service" to DeclCfg( + "This defines a service singleton which will be started up along side your service", + arrayOf(), + arrayOf(), + ), + "encore:authhandler" to DeclCfg( + "This defines an authentication handler which will be used to authenticate requests for your whole application.", + arrayOf(), + arrayOf(), + ), + "encore:middleware" to DeclCfg( + "This defines a middleware which will be used to process requests", + arrayOf("global"), + arrayOf("target"), + ) +) + +fun isApiAnnotation(comment: PsiComment) = parseApiAnnotation(comment).second != null + +private fun parseApiAnnotation(comment: PsiComment): Triple> { + val text = comment.text + if (!text.startsWith(API_DECL_PREFIX)) { + return Triple("", null, emptyList()) } + val parts = text.removePrefix("//").split(" ") + val prefix = parts.getOrNull(0) ?: "" + + return Triple(prefix, PREFIXES[prefix], parts.drop(1)) } data class DeclCfg( diff --git a/src/main/kotlin/dev/encore/intellij/inspections/ApiUnusedSuppressor.kt b/src/main/kotlin/dev/encore/intellij/inspections/ApiUnusedSuppressor.kt new file mode 100644 index 0000000..93a2da2 --- /dev/null +++ b/src/main/kotlin/dev/encore/intellij/inspections/ApiUnusedSuppressor.kt @@ -0,0 +1,28 @@ +package dev.encore.intellij.inspections + +import com.goide.psi.GoFunctionDeclaration +import com.intellij.codeInspection.InspectionSuppressor +import com.intellij.codeInspection.SuppressQuickFix +import com.intellij.psi.PsiComment +import com.intellij.psi.PsiElement +import com.intellij.psi.util.PsiTreeUtil +import dev.encore.intellij.annotators.isApiAnnotation + +class ApiUnusedSuppressor : InspectionSuppressor { + + override fun isSuppressedFor(element: PsiElement, toolId: String): Boolean { + if (element !is GoFunctionDeclaration || toolId != TOOL_ID) { + return false + } + val prevElement = PsiTreeUtil.skipWhitespacesBackward(element) + if (prevElement !is PsiComment) { + return false + } + val comment: PsiComment = prevElement + return isApiAnnotation(comment) + } + + override fun getSuppressActions(element: PsiElement?, toolId: String) = SuppressQuickFix.EMPTY_ARRAY!! +} + +private const val TOOL_ID = "GoUnusedExportedFunction" diff --git a/src/main/resources/META-INF/plugin.xml b/src/main/resources/META-INF/plugin.xml index 5f93fa5..5142ce5 100644 --- a/src/main/resources/META-INF/plugin.xml +++ b/src/main/resources/META-INF/plugin.xml @@ -52,6 +52,8 @@ id="dev.encore.intellij.settings.Settings" displayName="Encore"/> /> + +