Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suppress GoLand unused function warning on API #17

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 0 additions & 17 deletions .idea/gradle.xml

This file was deleted.

9 changes: 4 additions & 5 deletions build.gradle.kts
Original file line number Diff line number Diff line change
@@ -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)
Expand All @@ -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
Expand All @@ -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
Expand Down
4 changes: 2 additions & 2 deletions gradle.properties
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand All @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion gradle/wrapper/gradle-wrapper.properties
Original file line number Diff line number Diff line change
@@ -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
103 changes: 57 additions & 46 deletions src/main/kotlin/dev/encore/intellij/annotators/ApiDecls.kt
Original file line number Diff line number Diff line change
@@ -1,55 +1,55 @@
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)
.tooltip(cfg.tooltip)
.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<String>()

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
Expand All @@ -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)) {
Expand All @@ -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
Expand Down Expand Up @@ -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<String, DeclCfg?, List<String>> {
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(
Expand Down
Original file line number Diff line number Diff line change
@@ -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"
2 changes: 2 additions & 0 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@
id="dev.encore.intellij.settings.Settings"
displayName="Encore"/>
/>

<lang.inspectionSuppressor language="go" implementationClass="dev.encore.intellij.inspections.ApiUnusedSuppressor"/>
</extensions>

<extensions defaultExtensionNs="com.goide">
Expand Down