Skip to content

Commit acdbdc7

Browse files
committed
early work
1 parent d50973b commit acdbdc7

File tree

4 files changed

+164
-18
lines changed

4 files changed

+164
-18
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
package dotty.tools.pc
2+
3+
import org.eclipse.lsp4j
4+
import org.eclipse.lsp4j.DiagnosticSeverity
5+
import dotty.tools.dotc.interactive.InteractiveDriver
6+
import dotty.tools.dotc.interfaces.Diagnostic as DiagnosticInterfaces
7+
import dotty.tools.dotc.reporting.Diagnostic
8+
import dotty.tools.dotc.semanticdb.DiagnosticOps.toSemanticDiagnostic
9+
import dotty.tools.pc.utils.InteractiveEnrichments.toLsp
10+
11+
import scala.meta.pc.VirtualFileParams
12+
import com.google.gson.Gson
13+
import ch.epfl.scala.bsp4j
14+
import dotty.tools.dotc.reporting.CodeAction
15+
import dotty.tools.dotc.rewrites.Rewrites.ActionPatch
16+
import scala.jdk.CollectionConverters.*
17+
import dotty.tools.dotc.core.Contexts.Context
18+
import dotty.tools.dotc.reporting.Message
19+
import dotty.tools.dotc.interfaces.DiagnosticRelatedInformation
20+
import org.eclipse.lsp4j.Location
21+
import dotty.tools.dotc.reporting.ErrorMessageID
22+
import org.eclipse.lsp4j.DiagnosticTag
23+
24+
class DiagnosticProvider(driver: InteractiveDriver, params: VirtualFileParams):
25+
26+
def diagnostics(): List[lsp4j.Diagnostic] =
27+
val diags = driver.run(params.uri().nn, params.text().nn)
28+
given Context = driver.currentCtx
29+
diags.flatMap(toLsp)
30+
31+
private def toLsp(diag: Diagnostic)(using Context): Option[lsp4j.Diagnostic] =
32+
Option.when(diag.pos.exists):
33+
val lspDiag = lsp4j.Diagnostic(
34+
diag.pos.toLsp,
35+
diag.msg.message,
36+
toDiagnosticSeverity(diag.level),
37+
"presentation compiler",
38+
)
39+
lspDiag.setCode(diag.msg.errorId.errorNumber)
40+
41+
val scalaDiagnostic = new bsp4j.ScalaDiagnostic()
42+
val actions = diag.msg.actions.map(toBspScalaAction).asJava
43+
// lspDiag.setRelatedInformation(???) Currently not emitted by the compiler
44+
lspDiag.setData(actions)
45+
if diag.msg.errorId == ErrorMessageID.UnusedSymbolID then
46+
lspDiag.setTags(List(DiagnosticTag.Unnecessary).asJava)
47+
48+
lspDiag
49+
50+
private def toBspScalaAction(action: CodeAction): bsp4j.ScalaAction =
51+
val bspAction = bsp4j.ScalaAction(action.title)
52+
action.description.foreach(bspAction.setDescription)
53+
val workspaceEdit = bsp4j.ScalaWorkspaceEdit(action.patches.map(toBspTextEdit).asJava)
54+
bspAction.setEdit(workspaceEdit)
55+
bspAction
56+
57+
private def toBspTextEdit(actionPatch: ActionPatch): bsp4j.ScalaTextEdit =
58+
val startPos = bsp4j.Position(actionPatch.srcPos.startLine, actionPatch.srcPos.startColumn)
59+
val endPos = bsp4j.Position(actionPatch.srcPos.endLine, actionPatch.srcPos.endColumn)
60+
val range = bsp4j.Range(startPos, endPos)
61+
bsp4j.ScalaTextEdit(range, actionPatch.replacement)
62+
63+
64+
private def toDiagnosticSeverity(severity: Int): DiagnosticSeverity =
65+
severity match
66+
case DiagnosticInterfaces.ERROR => DiagnosticSeverity.Error
67+
case DiagnosticInterfaces.WARNING => DiagnosticSeverity.Warning
68+
case DiagnosticInterfaces.INFO => DiagnosticSeverity.Information
69+
case _ => DiagnosticSeverity.Information
70+

presentation-compiler/src/main/dotty/tools/pc/ScalaPresentationCompiler.scala

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import java.util.Optional
77
import java.util.concurrent.CompletableFuture
88
import java.util.concurrent.ExecutorService
99
import java.util.concurrent.ScheduledExecutorService
10+
import java.util.Collections
1011
import java.util as ju
1112

1213
import scala.concurrent.ExecutionContext
@@ -108,7 +109,9 @@ case class ScalaPresentationCompiler(
108109
params.token()
109110
) { access =>
110111
val driver = access.compiler()
111-
new PcSemanticTokensProvider(driver, params).provide().asJava
112+
new PcSemanticTokensProvider(driver, params)
113+
.provide()
114+
.asJava
112115
}
113116

114117
override def inlayHints(
@@ -175,7 +178,7 @@ case class ScalaPresentationCompiler(
175178
params: OffsetParams
176179
): CompletableFuture[ju.List[DocumentHighlight]] =
177180
compilerAccess.withInterruptableCompiler(Some(params))(
178-
List.empty[DocumentHighlight].asJava,
181+
Collections.emptyList(),
179182
params.token()
180183
) { access =>
181184
val driver = access.compiler()
@@ -186,7 +189,7 @@ case class ScalaPresentationCompiler(
186189
params: ReferencesRequest
187190
): CompletableFuture[ju.List[ReferencesResult]] =
188191
compilerAccess.withNonInterruptableCompiler(Some(params.file()))(
189-
List.empty[ReferencesResult].asJava,
192+
Collections.emptyList(),
190193
params.file().token,
191194
) { access =>
192195
val driver = access.compiler()
@@ -204,14 +207,11 @@ case class ScalaPresentationCompiler(
204207
new InferExpectedType(search, driver, params).infer().asJava
205208
}
206209

207-
def shutdown(): Unit =
208-
compilerAccess.shutdown()
210+
def shutdown(): Unit = compilerAccess.shutdown()
209211

210-
def restart(): Unit =
211-
compilerAccess.shutdownCurrentCompiler()
212+
def restart(): Unit = compilerAccess.shutdownCurrentCompiler()
212213

213-
def diagnosticsForDebuggingPurposes(): ju.List[String] =
214-
List[String]().asJava
214+
def diagnosticsForDebuggingPurposes(): ju.List[String] = Collections.emptyList()
215215

216216
override def info(
217217
symbol: String
@@ -264,7 +264,7 @@ case class ScalaPresentationCompiler(
264264
ju.List[scala.meta.pc.AutoImportsResult]
265265
] =
266266
compilerAccess.withNonInterruptableCompiler(Some(params))(
267-
List.empty[scala.meta.pc.AutoImportsResult].asJava,
267+
Collections.emptyList(),
268268
params.token()
269269
) { access =>
270270
val driver = access.compiler()
@@ -283,9 +283,8 @@ case class ScalaPresentationCompiler(
283283
def implementAbstractMembers(
284284
params: OffsetParams
285285
): CompletableFuture[ju.List[l.TextEdit]] =
286-
val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]()
287286
compilerAccess.withNonInterruptableCompiler(Some(params))(
288-
empty,
287+
Collections.emptyList(),
289288
params.token()
290289
) { pc =>
291290
val driver = pc.compiler()
@@ -301,9 +300,8 @@ case class ScalaPresentationCompiler(
301300
override def insertInferredType(
302301
params: OffsetParams
303302
): CompletableFuture[ju.List[l.TextEdit]] =
304-
val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]()
305303
compilerAccess.withNonInterruptableCompiler(Some(params))(
306-
empty,
304+
Collections.emptyList(),
307305
params.token()
308306
) { pc =>
309307
new InferredTypeProvider(params, pc.compiler(), config, search)
@@ -330,8 +328,10 @@ case class ScalaPresentationCompiler(
330328
range: RangeParams,
331329
extractionPos: OffsetParams
332330
): CompletableFuture[ju.List[l.TextEdit]] =
333-
val empty: ju.List[l.TextEdit] = new ju.ArrayList[l.TextEdit]()
334-
compilerAccess.withInterruptableCompiler(Some(range))(empty, range.token()) {
331+
compilerAccess.withInterruptableCompiler(Some(range))(
332+
Collections.emptyList(),
333+
range.token()
334+
) {
335335
pc =>
336336
new ExtractMethodProvider(
337337
range,
@@ -368,7 +368,7 @@ case class ScalaPresentationCompiler(
368368
): CompletableFuture[ju.List[l.SelectionRange]] =
369369
CompletableFuture.completedFuture {
370370
compilerAccess.withSharedCompiler(params.asScala.headOption)(
371-
List.empty[l.SelectionRange].asJava
371+
Collections.emptyList()
372372
) { pc =>
373373
new SelectionRangeProvider(
374374
pc.compiler(),
@@ -438,7 +438,13 @@ case class ScalaPresentationCompiler(
438438
override def didChange(
439439
params: VirtualFileParams
440440
): CompletableFuture[ju.List[l.Diagnostic]] =
441-
CompletableFuture.completedFuture(Nil.asJava)
441+
compilerAccess.withNonInterruptableCompiler(Some(params))(
442+
Collections.emptyList(),
443+
params.token()
444+
) { access =>
445+
val driver = access.compiler()
446+
new DiagnosticProvider(driver, params).diagnostics().asJava
447+
}
442448

443449
override def didClose(uri: URI): Unit =
444450
compilerAccess.withNonInterruptableCompiler(None)(
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
package dotty.tools.pc.tests
2+
3+
import scala.language.unsafeNulls
4+
5+
import dotty.tools.pc.base.BasePCSuite
6+
import dotty.tools.pc.utils.RangeReplace
7+
8+
import java.net.URI
9+
import org.eclipse.lsp4j.jsonrpc.messages.{Either => JEither}
10+
import scala.meta.internal.jdk.CollectionConverters.*
11+
import scala.meta.internal.metals.CompilerVirtualFileParams
12+
import scala.meta.internal.metals.EmptyCancelToken
13+
import scala.meta.internal.pc.PcReferencesRequest
14+
15+
import org.junit.Test
16+
import scala.collection.mutable.ListBuffer
17+
import org.eclipse.lsp4j.DiagnosticSeverity
18+
import scala.concurrent.duration.*
19+
import dotty.tools.pc.utils.TestExtensions.getOffset
20+
21+
class DiagnosticProviderSuite extends BasePCSuite with RangeReplace {
22+
private val rangeRegex = "<<.*>>".r
23+
case class TestDiagnostic(startIndex: Int, endIndex: Int, msg: String, severity: DiagnosticSeverity)
24+
25+
def check(
26+
text: String,
27+
expected: List[TestDiagnostic]
28+
): Unit =
29+
val diagnostics = presentationCompiler
30+
.didChange(CompilerVirtualFileParams(URI.create("file:/Diagnostic.scala"), text, EmptyCancelToken))
31+
.get()
32+
.asScala
33+
34+
val actual = diagnostics.map(d => TestDiagnostic(d.getRange().getStart().getOffset(text), d.getRange().getEnd().getOffset(text), d.getMessage(), d.getSeverity()))
35+
assertEquals(expected, actual, s"Expected [${expected.mkString(", ")}] but got [${actual.mkString(", ")}]")
36+
37+
@Test def simple1 =
38+
check(
39+
"""|class Bar(i: It)
40+
|""".stripMargin,
41+
List(TestDiagnostic(13, 15, "Not found: type It - did you mean Int.type? or perhaps Int?", DiagnosticSeverity.Error))
42+
)
43+
44+
// @Test def `implicit-args-2` =
45+
// check(
46+
// """|package example
47+
// |
48+
// |class Bar(i: Int)
49+
// |class Foo(implicit b: Bar)
50+
// |
51+
// |object Hello {
52+
// | implicit val ba@@rr: Bar = new Bar(1)
53+
// | val foo = new Foo<<>>
54+
// |}
55+
// |""".stripMargin
56+
// )
57+
58+
// @Test def `case-class` =
59+
// check(
60+
// """|case class Ma@@in(i: Int)
61+
// |""".stripMargin
62+
// )
63+
64+
// @Test def `case-class-with-implicit` =
65+
// check(
66+
// """"|case class A()(implicit val fo@@o: Int)
67+
// |""".stripMargin
68+
// )
69+
}

project/Build.scala

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1487,6 +1487,7 @@ object Build {
14871487
.exclude("org.eclipse.lsp4j","org.eclipse.lsp4j")
14881488
.exclude("org.eclipse.lsp4j","org.eclipse.lsp4j.jsonrpc"),
14891489
"org.eclipse.lsp4j" % "org.eclipse.lsp4j" % "0.20.1",
1490+
"ch.epfl.scala" % "bsp4j" % "2.1.1",
14901491
),
14911492
libraryDependencies += ("org.scalameta" % "mtags-shared_2.13.15" % mtagsVersion % SourceDeps),
14921493
ivyConfigurations += SourceDeps.hide,

0 commit comments

Comments
 (0)