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

fix!: use --stdin haml-lint option #105

Merged
merged 8 commits into from
Jan 16, 2025
Merged
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
8 changes: 0 additions & 8 deletions build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ repositories {
dependencies {
implementation(libs.gson)
testImplementation(kotlin("test"))
testImplementation(libs.mockitoCore)
mockitoAgent(libs.mockitoCore) { isTransitive = false }
testImplementation(libs.mockitoInline)
testImplementation(libs.mockitoKotlin)

// IntelliJ Platform Gradle Plugin Dependencies Extension - read more: https://plugins.jetbrains.com/docs/intellij/tools-intellij-platform-gradle-plugin-dependencies-extension.html
intellijPlatform {
Expand Down Expand Up @@ -144,10 +140,6 @@ tasks {
dependsOn(patchChangelog)
}
*/

test {
jvmArgs("-javaagent:${mockitoAgent.asPath}")
}
}

intellijPlatformTesting {
Expand Down
6 changes: 0 additions & 6 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
[versions]
# libraries
gson= "2.11.0"
mockitoCore = "5.15.2"
mockitoInline = "5.2.0"
mockitoKotlin = "5.4.0"

# plugins
changelog = "2.2.1"
Expand All @@ -13,9 +10,6 @@ ktlint = "12.1.2"

[libraries]
gson = { group = "com.google.code.gson", name = "gson", version.ref = "gson" }
mockitoCore = { group = "org.mockito", name = "mockito-core", version.ref = "mockitoCore" }
mockitoInline = { group = "org.mockito", name = "mockito-inline", version.ref = "mockitoInline" }
mockitoKotlin = { group = "org.mockito.kotlin", name = "mockito-kotlin", version.ref = "mockitoKotlin" }

[plugins]
changelog = { id = "org.jetbrains.changelog", version.ref = "changelog" }
Expand Down
32 changes: 18 additions & 14 deletions src/main/kotlin/me/benmelz/jetbrains/plugins/hamllint/HamlLint.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package me.benmelz.jetbrains.plugins.hamllint

import com.google.gson.JsonParser
import com.intellij.execution.configurations.GeneralCommandLine
import com.intellij.execution.process.OSProcessHandler
import com.intellij.execution.process.ScriptRunnerUtil
import java.io.File
import java.nio.charset.StandardCharsets
Expand All @@ -11,42 +12,45 @@ import java.util.LinkedList
/**
* Executes haml-lint externally using the command line and parses its output.
*
* @param[haml] the raw haml text to run against.
* @param[workDirectory] the work directory from which to run haml-lint.
* @param[executionCommand] the execution command with which to run haml-lint.
* @param[haml] raw haml code to lint.
* @param[filePath] path to the file that is being linted.
* @param[workDirectory] work directory from which to run haml-lint.
* @param[executionCommand] execution command with which to run haml-lint.
* @return a list of collected haml-lint offenses.
*/
fun hamlLint(
haml: CharSequence,
filePath: Path,
workDirectory: Path,
executionCommand: List<String>,
): List<HamlLintOffense> {
HamlLintTarget.createTempTarget(haml).use {
val cli = hamlLintCommandLine(it, workDirectory, executionCommand)
return parseHamlLintOutput(ScriptRunnerUtil.getProcessOutput(cli))
}
val cli = hamlLintCommandLine(filePath, workDirectory, executionCommand)
val processHandler = OSProcessHandler(cli)
val stdin = processHandler.processInput
haml.forEach { stdin.write(it.code) }
stdin.close()
return parseHamlLintOutput(
ScriptRunnerUtil.getProcessOutput(processHandler, ScriptRunnerUtil.STDOUT_OUTPUT_KEY_FILTER, 30000L),
)
}

/**
* Builds an executable [GeneralCommandLine] that runs `haml-lint` against a given [HamlLintTarget] within a given
* working directory within the context of `bundler`.
* Builds an executable [GeneralCommandLine] that runs `haml-lint` in stdin mode and the json reporter.
*
* @param[target] the [HamlLintTarget] to run against.
* @param[filePath] original file path for the code that is being linted.
* @param[workDirectory] the directory from which to run `haml-lint`.
* @param[executionCommand] the execution command with which to run haml-lint.
* @return an executable [GeneralCommandLine].
*/
private fun hamlLintCommandLine(
target: HamlLintTarget,
filePath: Path,
workDirectory: Path,
executionCommand: List<String>,
): GeneralCommandLine =
GeneralCommandLine(executionCommand).apply {
this.addParameters("--reporter", "json", target.absolutePath)
this.addParameters("--stdin", filePath.toString(), "--reporter", "json")
this.charset = StandardCharsets.UTF_8
this.workDirectory = File(workDirectory.toUri())
val rubocopConfigPath = workDirectory.resolve(".rubocop.yml").toAbsolutePath().toString()
if (File(rubocopConfigPath).exists()) this.withEnvironment("HAML_LINT_RUBOCOP_CONF", rubocopConfigPath)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@ class HamlLintExternalAnnotator : ExternalAnnotator<HamlLintExternalAnnotatorInf
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.
* Collects `haml` code as a string, the current file path, the root path of the parent project and the configured
* execution command.
*
* @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.
Expand All @@ -32,15 +33,16 @@ class HamlLintExternalAnnotator : ExternalAnnotator<HamlLintExternalAnnotatorInf
if (!(configuration.enabled)) return null

val fileText = file.viewProvider.document.charsSequence
val filePath = file.virtualFile.toNioPath()

val contentRoot =
ProjectFileIndex.getInstance(file.project).getContentRootForFile(file.virtualFile)?.toNioPath()
if (contentRoot == null) return null
?: return null

val executionCommand = configuration.executionCommand.split(" ").filter { it.isNotEmpty() }
if (executionCommand.isEmpty()) return null

return HamlLintExternalAnnotatorInfo(fileText, contentRoot, executionCommand)
return HamlLintExternalAnnotatorInfo(fileText, filePath, contentRoot, executionCommand)
}

/**
Expand All @@ -54,7 +56,12 @@ class HamlLintExternalAnnotator : ExternalAnnotator<HamlLintExternalAnnotatorInf
null
} else {
try {
hamlLint(collectedInfo.fileText, collectedInfo.contentRoot, collectedInfo.executionCommand)
hamlLint(
collectedInfo.fileText,
collectedInfo.filePath,
collectedInfo.contentRoot,
collectedInfo.executionCommand,
)
} catch (e: ExecutionException) {
logger.error(e.message)
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,14 @@ import java.nio.file.Path
/**
* Information collected for a run of [HamlLintExternalAnnotator.doAnnotate].
*
* @property[fileText] raw `haml` code to lint using `haml-lint`.
* @property[contentRoot] the directory of the parent project of the code to be linted.
* @param[executionCommand] the execution command with which to run haml-lint.
* @property[fileText] raw `haml` code to lint.
* @property[filePath] path to the file that is to be linted.
* @property[contentRoot] directory of the parent project of the code to be linted.
* @param[executionCommand] execution command with which to run haml-lint.
*/
data class HamlLintExternalAnnotatorInfo(
val fileText: CharSequence,
val filePath: Path,
val contentRoot: Path,
val executionCommand: List<String>,
)

This file was deleted.

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,31 +1,12 @@
package me.benmelz.jetbrains.plugins.hamllint

import com.intellij.execution.process.ScriptRunnerUtil
import org.mockito.Mockito.mockStatic
import org.mockito.kotlin.any
import java.nio.file.Path
import kotlin.test.Test
import kotlin.test.assertEquals

internal class HamlLintTest {
@Test
fun testHamlLint() {
val hamlLintOutputJson =
HamlLintTest::class.java
.classLoader
.getResource("haml_lint_output.json")!!
.readText()
mockStatic(ScriptRunnerUtil::class.java).use { mockedScriptRunnerUtil ->
mockedScriptRunnerUtil
.`when`<Any> {
ScriptRunnerUtil.getProcessOutput(any())
}.thenReturn(hamlLintOutputJson)

val offenses = hamlLint("test haml", Path.of("/"), listOf("bundle", "exec", "haml-lint"))
assertEquals(offenses.size, 3)
assertEquals(offenses[0], HamlLintOffense(0, "warning", "TestOffense0", "test offense 0"))
assertEquals(offenses[1], HamlLintOffense(1, "warning", "TestOffense1", "test offense 1"))
assertEquals(offenses[2], HamlLintOffense(2, "error", "TestOffense2", "test offense 2"))
}
fun testNothing() {
val value = true
assertEquals(value, true)
}
}
44 changes: 0 additions & 44 deletions src/test/resources/haml_lint_output.json

This file was deleted.

Loading