Skip to content

Commit

Permalink
feat: ability to configure highlight severities (#13)
Browse files Browse the repository at this point in the history
- adds severity mapping options to the inspection profile
- uses inspection severity mappings to translate `haml-lint` severities
to highlight severities
  • Loading branch information
benmelz authored May 1, 2023
1 parent 4336bb7 commit da3d879
Show file tree
Hide file tree
Showing 4 changed files with 114 additions and 21 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
package me.benmelz.jetbrains.plugins.hamllint

import com.intellij.codeInspection.ex.Tools
import com.intellij.lang.annotation.AnnotationHolder
import com.intellij.lang.annotation.ExternalAnnotator
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.openapi.editor.Document
import com.intellij.openapi.diagnostic.Logger
import com.intellij.openapi.roots.ProjectFileIndex
import com.intellij.openapi.util.TextRange
import com.intellij.profile.codeInspection.ProjectInspectionProfileManager
import com.intellij.profile.codeInspection.InspectionProfileManager
import com.intellij.psi.PsiFile

/**
Expand All @@ -15,16 +16,19 @@ import com.intellij.psi.PsiFile
* @see ExternalAnnotator
*/
class HamlLintExternalAnnotator : ExternalAnnotator<HamlLintExternalAnnotatorInfo, List<HamlLintOffense>>() {
/**
* The global logger instance for the plugin.
*/
private val logger = Logger.getInstance("HamlLint")

/**
* Collects `haml` code as a string as well as the path to the parent project of a file to lint.
*
* @param[file] the file to run `haml-lint` against.
* @return the necessary information to run `haml-lint` against a file, `null` if the file should be skipped.
*/
override fun collectInformation(file: PsiFile): HamlLintExternalAnnotatorInfo? {
val inspectionProfile = ProjectInspectionProfileManager.getInstance(file.project).currentProfile
val inspectionToolDisplayKey = inspectionProfile.getInspectionTool("HamlLint", file)?.displayKey
if (!inspectionProfile.isToolEnabled(inspectionToolDisplayKey, file)) return null
if (!(inspectionTool(file).isEnabled)) return null
val fileText = file.viewProvider.document.charsSequence
val contentRoot = ProjectFileIndex
.getInstance(file.project)
Expand All @@ -51,22 +55,36 @@ class HamlLintExternalAnnotator : ExternalAnnotator<HamlLintExternalAnnotatorInf
* @param[holder] a holder for any annotations to display in the editor.
*/
override fun apply(file: PsiFile, offenses: List<HamlLintOffense>?, holder: AnnotationHolder) {
val severityMap = buildHighlightSeverityMap()
offenses?.forEach {
val severity = translateOffenseSeverity(it.severity)
val severity = translateOffenseSeverity(it.severity, file, severityMap)
val message = translateOffenseLinterNameAndMessage(it.linterName, it.message)
val range = translateOffenseLineNumber(it.lineNumber, file.viewProvider.document)
if (range != null) holder.newAnnotation(severity, message).range(range).create()
val range = translateOffenseLineNumber(it.lineNumber, file)
if (severity != null && range != null) holder.newAnnotation(severity, message).range(range).create()
}
}

/**
* Translates a `haml-lint` severity to a [HighlightSeverity].
* Translates a `haml-lint` severity to a [HighlightSeverity] based on the inspection configuring.
*
* @param[severity] the `haml-lint` severity reported by an offense.
* @return an equivalent [HighlightSeverity].
*/
private fun translateOffenseSeverity(severity: String): HighlightSeverity {
return if (severity == "error") HighlightSeverity.ERROR else HighlightSeverity.WEAK_WARNING
private fun translateOffenseSeverity(
severity: String,
file: PsiFile,
severityMap: Map<String, HighlightSeverity>,
): HighlightSeverity? {
val inspectionTool = inspectionProfileEntry(file)
val severityKey = when (severity) {
"warning" -> inspectionTool.warningSeverityKey
"error" -> inspectionTool.errorSeverityKey
else -> {
logger.error("Unrecognized severity: $severity")
null
}
}
return severityMap[severityKey]
}

/**
Expand All @@ -84,10 +102,11 @@ class HamlLintExternalAnnotator : ExternalAnnotator<HamlLintExternalAnnotatorInf
* Translates a line number of a `haml-lint` offense to a [TextRange] for highlighting.
*
* @param[lineNumber] the number of the line containing the offense.
* @param[document] the document of the file that was linted.
* @param[document] the file that was linted.
* @return a text range for the exact characters to highlight.
*/
private fun translateOffenseLineNumber(lineNumber: Int, document: Document): TextRange? {
private fun translateOffenseLineNumber(lineNumber: Int, file: PsiFile): TextRange? {
val document = file.viewProvider.document
val lineIndex = if (lineNumber <= 0) 0 else lineNumber - 1
if (lineIndex >= document.lineCount) return null
var startOffset = document.getLineStartOffset(lineIndex)
Expand All @@ -97,4 +116,33 @@ class HamlLintExternalAnnotator : ExternalAnnotator<HamlLintExternalAnnotatorInf
while (documentText[startOffset] == ' ' && startOffset < endOffset) startOffset++
return TextRange(startOffset, endOffset)
}

/**
* Retrieves the top-level wrapper of a file's project's [HamlLintInspection].
*
* @param[file] the file whose inspection tool wrapper to retrieve.
* @return the top-level wrapper for the inspection tool.
*/
private fun inspectionTool(file: PsiFile): Tools {
return InspectionProfileManager.getInstance(file.project).currentProfile.getTools("HamlLint", file.project)
}

/**
* Retrieves a file's project's [HamlLintInspection] instance.
*
* @param[file] the file inspection instance to retrieve.
* @return the [HamlLintInspection] instance of the given file.
*/
private fun inspectionProfileEntry(file: PsiFile): HamlLintInspection {
return inspectionTool(file).getInspectionTool(file).tool as HamlLintInspection
}

/**
* Builds a mapping of all highlight severities by name.
*
* @return a mapping of highlight severities by name.
*/
private fun buildHighlightSeverityMap(): Map<String, HighlightSeverity> {
return InspectionProfileManager.getInstance().severityRegistrar.allSeverities.associateBy { it.name }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,12 @@ package me.benmelz.jetbrains.plugins.hamllint
import com.intellij.codeInspection.ExternalAnnotatorInspectionVisitor
import com.intellij.codeInspection.LocalInspectionTool
import com.intellij.codeInspection.ProblemsHolder
import com.intellij.codeInspection.options.OptPane
import com.intellij.codeInspection.options.OptPane.dropdown
import com.intellij.codeInspection.options.OptPane.group
import com.intellij.codeInspection.options.OptPane.option
import com.intellij.codeInspection.options.OptPane.pane
import com.intellij.lang.annotation.HighlightSeverity
import com.intellij.psi.PsiElementVisitor

/**
Expand All @@ -13,6 +19,16 @@ import com.intellij.psi.PsiElementVisitor
* @see LocalInspectionTool
*/
class HamlLintInspection : LocalInspectionTool() {
/**
* The name of the highlight severity to use for `haml-lint` errors.
*/
var errorSeverityKey: String = HighlightSeverity.ERROR.name

/**
* The name of the highlight severity to use for `haml-lint` warnings.
*/
var warningSeverityKey: String = HighlightSeverity.WEAK_WARNING.name

/**
* Delegates inspection logic to a [HamlLintExternalAnnotator].
*
Expand All @@ -30,4 +46,27 @@ class HamlLintInspection : LocalInspectionTool() {
* @return false.
*/
override fun showDefaultConfigurationOptions(): Boolean = false

/**
* Builds the configuration pane for the inspection/plugin.
*
* @return a set of UI elements used to control the plugin and its settings.
*/
override fun getOptionsPane(): OptPane {
val severityOptions = arrayOf(
*arrayOf(
HighlightSeverity.ERROR,
HighlightSeverity.WARNING,
HighlightSeverity.WEAK_WARNING,
).map { option(it.name, it.displayCapitalizedName) }.toTypedArray(),
option("", "No highlighting"),
)
return pane(
group(
"HamlLint Severities Mapping",
dropdown("errorSeverityKey", "Error: ", *severityOptions),
dropdown("warningSeverityKey", "Warning: ", *severityOptions),
),
)
}
}
11 changes: 6 additions & 5 deletions src/main/resources/META-INF/plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -15,15 +15,16 @@
Guidelines: https://plugins.jetbrains.com/docs/marketplace/plugin-overview-page.html#plugin-description -->
<description><![CDATA[
<p>
Provides realtime inspection of <code><a href="https://github.com/sds/haml-lint">haml-lint</a></code> offenses.
Provides realtime inspection of <code><a href="https://github.com/sds/haml-lint">haml-lint</a></code> offenses.
</p>
<p>
No setup is necessary from the IDE. As long as <code>haml-lint</code> is included in your project's
<code>Gemfile</code>, the plugin will automatically annotate <code>.haml</code> files for you while you have
them open.
No setup is necessary from the IDE. As long as <code>haml-lint</code> is included in your project's
<code>Gemfile</code>, the plugin will automatically annotate <code>.haml</code> files for you while you have
them open.
</p>
<p>
The inspector can be turned on/off using the inspections menu (Haml -> HamlLint).
The inspector can be turned on/off and the severity mappings can be customized through the inspections menu
(<code>Haml</code> -> <code>HamlLint</code>).
</p>
]]></description>

Expand Down
11 changes: 8 additions & 3 deletions src/main/resources/inspectionDescriptions/HamlLint.html
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
<html>
<html lang="en">
<body>
Reports issues from the <a href="https://github.com/sds/haml-lint">HamlLint</a>. linter.
Requires the haml-lint gem to be installed in the project SDK.
<p>
Reports issues from the <a href="https://github.com/sds/haml-lint">HamlLint</a>. linter.
Requires the haml-lint gem to be installed in the project SDK.
</p>
<p>
Use the <em>HamlLint Severities Mapping</em> fields to customize the highlight levels.
</p>
</body>
</html>

0 comments on commit da3d879

Please sign in to comment.