@@ -194,6 +194,7 @@ class DottyLanguageServer extends LanguageServer
194
194
c.setHoverProvider(true )
195
195
c.setWorkspaceSymbolProvider(true )
196
196
c.setReferencesProvider(true )
197
+ c.setImplementationProvider(true )
197
198
c.setCompletionProvider(new CompletionOptions (
198
199
/* resolveProvider = */ false ,
199
200
/* triggerCharacters = */ List (" ." ).asJava))
@@ -313,39 +314,20 @@ class DottyLanguageServer extends LanguageServer
313
314
314
315
val pos = sourcePosition(driver, uri, params.getPosition)
315
316
316
- val (definitions, projectsToInspect, originalSymbol, originalSymbolName) = {
317
+ val (definitions, originalSymbol, originalSymbolName) = {
317
318
implicit val ctx : Context = driver.currentCtx
318
319
val path = Interactive .pathTo(driver.openedTrees(uri), pos)
319
320
val originalSymbol = Interactive .enclosingSourceSymbol(path)
320
321
val originalSymbolName = originalSymbol.name.sourceModuleName.toString
321
-
322
- // Find definitions of the symbol under the cursor, so that we can determine
323
- // what projects are worth exploring
324
322
val definitions = Interactive .findDefinitions(path, driver)
325
- val projectsToInspect =
326
- if (definitions.isEmpty) {
327
- drivers.keySet
328
- } else {
329
- for {
330
- definition <- definitions
331
- uri <- toUriOption(definition.pos.source).toSet
332
- config = configFor(uri)
333
- project <- dependentProjects(config) + config
334
- } yield project
335
- }
336
323
337
- (definitions, projectsToInspect, originalSymbol, originalSymbolName)
324
+ (definitions, originalSymbol, originalSymbolName)
338
325
}
339
326
340
327
val references = {
341
328
// Collect the information necessary to look into each project separately: representation of
342
329
// `originalSymbol` in this project, the context and correct Driver.
343
- val perProjectInfo = projectsToInspect.toList.map { config =>
344
- val remoteDriver = drivers(config)
345
- val ctx = remoteDriver.currentCtx
346
- val definition = Interactive .localize(originalSymbol, driver, remoteDriver)
347
- (remoteDriver, ctx, definition)
348
- }
330
+ val perProjectInfo = inProjectsSeeing(driver, definitions, originalSymbol)
349
331
350
332
perProjectInfo.flatMap { (remoteDriver, ctx, definition) =>
351
333
val trees = remoteDriver.sourceTreesContaining(originalSymbolName)(ctx)
@@ -447,6 +429,34 @@ class DottyLanguageServer extends LanguageServer
447
429
}.asJava
448
430
}
449
431
432
+ override def implementation (params : TextDocumentPositionParams ) = computeAsync { cancelToken =>
433
+ val uri = new URI (params.getTextDocument.getUri)
434
+ val driver = driverFor(uri)
435
+
436
+ val pos = sourcePosition(driver, uri, params.getPosition)
437
+
438
+ val (definitions, originalSymbol) = {
439
+ implicit val ctx : Context = driver.currentCtx
440
+ val path = Interactive .pathTo(driver.openedTrees(uri), pos)
441
+ val originalSymbol = Interactive .enclosingSourceSymbol(path)
442
+ val definitions = Interactive .findDefinitions(path, driver)
443
+ (definitions, originalSymbol)
444
+ }
445
+
446
+ val implementations = {
447
+ val perProjectInfo = inProjectsSeeing(driver, definitions, originalSymbol)
448
+
449
+ perProjectInfo.flatMap { (remoteDriver, ctx, definition) =>
450
+ val trees = remoteDriver.sourceTrees(ctx)
451
+ val predicate = Interactive .implementationFilter(definition)(ctx)
452
+ val matches = Interactive .namedTrees(trees, includeReferences = false , predicate)(ctx)
453
+ matches.map(tree => location(tree.namePos(ctx), positionMapperFor(tree.source)))
454
+ }
455
+ }.toList
456
+
457
+ implementations.flatten.asJava
458
+ }
459
+
450
460
override def getTextDocumentService : TextDocumentService = this
451
461
override def getWorkspaceService : WorkspaceService = this
452
462
@@ -460,6 +470,48 @@ class DottyLanguageServer extends LanguageServer
460
470
override def resolveCodeLens (params : CodeLens ) = null
461
471
override def resolveCompletionItem (params : CompletionItem ) = null
462
472
override def signatureHelp (params : TextDocumentPositionParams ) = null
473
+
474
+ /**
475
+ * Find the set of projects that have any of `definitions` on their classpath.
476
+ *
477
+ * @param definitions The definitions to consider when looking for projects.
478
+ * @return The set of projects that have any of `definitions` on their classpath.
479
+ */
480
+ private def projectsSeeing (definitions : List [SourceTree ])(implicit ctx : Context ): Set [ProjectConfig ] = {
481
+ if (definitions.isEmpty) {
482
+ drivers.keySet
483
+ } else {
484
+ for {
485
+ definition <- definitions.toSet
486
+ uri <- toUriOption(definition.pos.source).toSet
487
+ config = configFor(uri)
488
+ project <- dependentProjects(config) + config
489
+ } yield project
490
+ }
491
+ }
492
+
493
+ /**
494
+ * Finds projects that can see any of `definitions`, translate `symbol` in their universe.
495
+ *
496
+ * @param baseDriver The driver responsible for the trees in `definitions` and `symbol`.
497
+ * @param definitions The definitions to consider when looking for projects.
498
+ * @param symbol A symbol to translate in the universes of the remote projects.
499
+ * @return A list consisting of the remote drivers, their context, and the translation of `symbol`
500
+ * into their universe.
501
+ */
502
+ private def inProjectsSeeing (baseDriver : InteractiveDriver ,
503
+ definitions : List [SourceTree ],
504
+ symbol : Symbol ): List [(InteractiveDriver , Context , Symbol )] = {
505
+ val projects = projectsSeeing(definitions)(baseDriver.currentCtx)
506
+ projects.toList.map { config =>
507
+ val remoteDriver = drivers(config)
508
+ val ctx = remoteDriver.currentCtx
509
+ val definition = Interactive .localize(symbol, baseDriver, remoteDriver)
510
+ (remoteDriver, ctx, definition)
511
+ }
512
+ }
513
+
514
+
463
515
}
464
516
465
517
object DottyLanguageServer {
0 commit comments