Skip to content
This repository was archived by the owner on Mar 27, 2023. It is now read-only.

Commit 9620098

Browse files
authored
Merge pull request #31 from kciesielski/fix-reporter
Load custom reporter in a plugin component
2 parents c2c6c13 + 7e54a2e commit 9620098

File tree

4 files changed

+76
-45
lines changed

4 files changed

+76
-45
lines changed

model/src/main/scala/com/softwaremill/clippy/StringDiff.scala

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,8 @@ package com.softwaremill.clippy
33
import com.softwaremill.clippy.StringDiff.{DeltaEnd, DeltaStart}
44

55
object StringDiff {
6-
val AnsiReset = "\u001B[0m"
7-
val AnsiRed = "\u001B[31m"
8-
val DeltaEnd = AnsiReset
9-
val DeltaStart = AnsiRed
6+
val DeltaEnd = Console.RESET
7+
val DeltaStart = Console.RED
108
}
119

1210
class StringDiff(expected: String, actual: String) {

plugin/src/main/scala/com/softwaremill/clippy/ClippyPlugin.scala

Lines changed: 42 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -13,40 +13,49 @@ class ClippyPlugin(val global: Global) extends Plugin {
1313

1414
override val name: String = "clippy"
1515

16-
override val components: List[PluginComponent] = Nil
17-
1816
override val description: String = "gives good advice"
1917

20-
override def processOptions(options: List[String], error: (String) => Unit) = {
21-
val r = global.reporter
18+
var url: String = ""
19+
var enableColors = false
20+
var testMode = false
21+
val DefaultStoreDir = new File(System.getProperty("user.home"), ".clippy")
22+
var localStoreDir = DefaultStoreDir
23+
var projectRoot: Option[File] = None
2224

23-
val url = urlFromOptions(options)
24-
val enableColors = colorsFromOptions(options)
25-
val localStoreDir = localStoreDirFromOptions(options)
26-
val projectRoot = projectRootFromOptions(options)
25+
def handleError(pos: Position, msg: String): String = {
2726
val advices = loadAdvices(url, localStoreDir, projectRoot)
27+
val parsedMsg = CompilationErrorParser.parse(msg)
28+
val matchers = advices.map(_.errMatching.lift)
29+
val matches = matchers.flatMap(pf => parsedMsg.flatMap(pf))
30+
31+
matches.size match {
32+
case 0 =>
33+
parsedMsg match {
34+
case Some(tme: TypeMismatchError[ExactT]) if enableColors => prettyPrintTypeMismatchError(tme, msg)
35+
case _ => msg
36+
}
37+
case 1 =>
38+
matches.mkString(s"$msg\n Clippy advises: ", "", "")
39+
case _ =>
40+
matches.mkString(s"$msg\n Clippy advises you to try one of these solutions: \n ", "\n or\n ", "")
41+
}
42+
}
43+
44+
override def processOptions(options: List[String], error: (String) => Unit): Unit = {
45+
enableColors = colorsFromOptions(options)
46+
url = urlFromOptions(options)
47+
testMode = testModeFromOptions(options)
48+
localStoreDir = localStoreDirFromOptions(options)
49+
projectRoot = projectRootFromOptions(options)
2850

29-
global.reporter = new DelegatingReporter(r, handleError)
30-
31-
def handleError(pos: Position, msg: String): String = {
32-
val parsedMsg = CompilationErrorParser.parse(msg)
33-
val matchers = advices.map(_.errMatching.lift)
34-
val matches = matchers.flatMap(pf => parsedMsg.flatMap(pf))
35-
36-
matches.size match {
37-
case 0 =>
38-
parsedMsg match {
39-
case Some(tme: TypeMismatchError[ExactT]) if enableColors => prettyPrintTypeMismatchError(tme, msg)
40-
case _ => msg
41-
}
42-
case 1 =>
43-
matches.mkString(s"$msg\n Clippy advises: ", "", "")
44-
case _ =>
45-
matches.mkString(s"$msg\n Clippy advises you to try one of these solutions: \n ", "\n or\n ", "")
46-
}
51+
if (testMode) {
52+
val r = global.reporter
53+
global.reporter = new DelegatingReporter(r, handleError)
4754
}
4855
}
4956

57+
override val components: List[PluginComponent] = List(new InjectReporter(handleError, global))
58+
5059
private def prettyPrintTypeMismatchError(tme: TypeMismatchError[ExactT], msg: String): String = {
5160
val plain = new StringDiff(tme.required.toString, tme.found.toString)
5261
val expands = new StringDiff(tme.requiredExpandsTo.toString, tme.foundExpandsTo.toString)
@@ -64,8 +73,12 @@ class ClippyPlugin(val global: Global) extends Plugin {
6473
private def urlFromOptions(options: List[String]): String =
6574
options.find(_.startsWith("url=")).map(_.substring(4)).getOrElse("https://www.scala-clippy.org") + "/api/advices"
6675

67-
private def colorsFromOptions(options: List[String]): Boolean =
68-
options.find(_.startsWith("colors=")).map(_.substring(7))
76+
private def colorsFromOptions(options: List[String]): Boolean = boolFromOptions(options, "colors")
77+
78+
private def testModeFromOptions(options: List[String]): Boolean = boolFromOptions(options, "testmode")
79+
80+
private def boolFromOptions(options: List[String], option: String): Boolean =
81+
options.find(_.startsWith(s"$option=")).map(_.substring(option.length + 1))
6982
.getOrElse("false")
7083
.toBoolean
7184

@@ -75,9 +88,7 @@ class ClippyPlugin(val global: Global) extends Plugin {
7588
.filter(_.exists())
7689

7790
private def localStoreDirFromOptions(options: List[String]): File =
78-
options.find(_.startsWith("store=")).map(_.substring(6)).map(new File(_)).getOrElse {
79-
new File(System.getProperty("user.home"), ".clippy")
80-
}
91+
options.find(_.startsWith("store=")).map(_.substring(6)).map(new File(_)).getOrElse(DefaultStoreDir)
8192

8293
private def loadAdvices(url: String, localStoreDir: File, projectAdviceFile: Option[File]): List[Advice] = {
8394
implicit val ec = scala.concurrent.ExecutionContext.Implicits.global
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
package com.softwaremill.clippy
2+
3+
import scala.reflect.internal.util.Position
4+
import scala.tools.nsc.plugins.PluginComponent
5+
import scala.tools.nsc.{Global, Phase}
6+
7+
class InjectReporter(handleError: (Position, String) => String, superGlobal: Global) extends PluginComponent {
8+
9+
override val global = superGlobal
10+
11+
override val runsAfter = List[String]("parser")
12+
override val runsBefore = List[String]("namer")
13+
override val phaseName = "inject-clippy-reporter"
14+
15+
override def newPhase(prev: Phase) = new Phase(prev) {
16+
17+
override def name = phaseName
18+
19+
override def description = "Switches the reporter to Clippy's DelegatingReporter"
20+
21+
override def run(): Unit = {
22+
val r = global.reporter
23+
global.reporter = new DelegatingReporter(r, handleError)
24+
}
25+
}
26+
27+
}

tests/src/test/scala/org/softwaremill/clippy/CompileTests.scala

Lines changed: 5 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,9 @@ package org.softwaremill.clippy
22

33
import java.io.{FileOutputStream, File}
44
import java.util.zip.GZIPOutputStream
5-
5+
import scala.reflect.runtime.currentMirror
66
import com.softwaremill.clippy._
77
import org.scalatest.{BeforeAndAfterAll, Matchers, FlatSpec}
8-
98
import scala.tools.reflect.ToolBox
109
import scala.tools.reflect.ToolBoxError
1110

@@ -136,23 +135,19 @@ class CompileTests extends FlatSpec with Matchers with BeforeAndAfterAll {
136135

137136
val tb = {
138137
val cpp = sys.env("CLIPPY_PLUGIN_PATH")
139-
140-
import scala.reflect.runtime._
141-
val cm = universe.runtimeMirror(getClass.getClassLoader)
142-
143-
cm.mkToolBox(options = s"-Xplugin:$cpp -Xplugin-require:clippy -P:clippy:colors=true")
138+
currentMirror.mkToolBox(options = s"-Xplugin:$cpp -Xplugin-require:clippy -P:clippy:colors=true -P:clippy:testmode=true")
144139
}
145140

146-
def parse(snippet: String) = tb.eval(tb.parse(snippet))
141+
def tryCompile(snippet: String) = tb.compile(tb.parse(snippet))
147142

148143
for ((name, s) <- snippets) {
149144
name should "compile with errors" in {
150-
(the[ToolBoxError] thrownBy parse(s)).message should include("Clippy advises")
145+
(the[ToolBoxError] thrownBy tryCompile(s)).message should include("Clippy advises")
151146
}
152147
}
153148

154149
"Clippy" should "return all matching advice" in {
155-
(the[ToolBoxError] thrownBy parse(snippets("macwire")))
150+
(the[ToolBoxError] thrownBy tryCompile(snippets("macwire")))
156151
.message should include("Clippy advises you to try one of these")
157152
}
158153

0 commit comments

Comments
 (0)