@@ -194,6 +194,7 @@ class DottyLanguageServer extends LanguageServer
194194 c.setHoverProvider(true )
195195 c.setWorkspaceSymbolProvider(true )
196196 c.setReferencesProvider(true )
197+ c.setImplementationProvider(true )
197198 c.setCompletionProvider(new CompletionOptions (
198199 /* resolveProvider = */ false ,
199200 /* triggerCharacters = */ List (" ." ).asJava))
@@ -313,39 +314,20 @@ class DottyLanguageServer extends LanguageServer
313314
314315 val pos = sourcePosition(driver, uri, params.getPosition)
315316
316- val (definitions, projectsToInspect, originalSymbol, originalSymbolName) = {
317+ val (definitions, originalSymbol, originalSymbolName) = {
317318 implicit val ctx : Context = driver.currentCtx
318319 val path = Interactive .pathTo(driver.openedTrees(uri), pos)
319320 val originalSymbol = Interactive .enclosingSourceSymbol(path)
320321 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
324322 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- }
336323
337- (definitions, projectsToInspect, originalSymbol, originalSymbolName)
324+ (definitions, originalSymbol, originalSymbolName)
338325 }
339326
340327 val references = {
341328 // Collect the information necessary to look into each project separately: representation of
342329 // `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)
349331
350332 perProjectInfo.flatMap { (remoteDriver, ctx, definition) =>
351333 val trees = remoteDriver.sourceTreesContaining(originalSymbolName)(ctx)
@@ -447,6 +429,34 @@ class DottyLanguageServer extends LanguageServer
447429 }.asJava
448430 }
449431
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+
450460 override def getTextDocumentService : TextDocumentService = this
451461 override def getWorkspaceService : WorkspaceService = this
452462
@@ -460,6 +470,48 @@ class DottyLanguageServer extends LanguageServer
460470 override def resolveCodeLens (params : CodeLens ) = null
461471 override def resolveCompletionItem (params : CompletionItem ) = null
462472 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+
463515}
464516
465517object DottyLanguageServer {
0 commit comments