All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.ammonite.compiler.AmmCompletion.scala Maven / Gradle / Ivy

package dotty.ammonite.compiler

import dotty.tools.dotc.ast.tpd._
import dotty.tools.dotc.ast.untpd
import dotty.tools.dotc.core.Contexts._
import dotty.tools.dotc.core.Denotations.SingleDenotation
import dotty.tools.dotc.core.Flags._
import dotty.tools.dotc.core.Names.Name
import dotty.tools.dotc.core.Symbols.{Symbol, defn}
import dotty.tools.dotc.interactive.{Completion, Interactive}
import dotty.tools.dotc.util.SourcePosition

object AmmCompletion extends AmmCompletionExtras {

  def completions(
    pos: SourcePosition,
    dependencyCompleteOpt: Option[String => (Int, Seq[String])],
    enableDeep: Boolean
  )(using Context): (Int, List[Completion]) = {
    val path = Interactive.pathTo(ctx.compilationUnit.tpdTree, pos.span)
    computeCompletions(
      pos,
      path,
      dependencyCompleteOpt,
      enableDeep
    )(using Interactive.contextOfPath(path))
  }

  def computeCompletions(
    pos: SourcePosition,
    path: List[Tree],
    dependencyCompleteOpt: Option[String => (Int, Seq[String])],
    enableDeep: Boolean
  )(using Context): (Int, List[Completion]) = {
    val mode = Completion.completionMode(path, pos)
    val prefix = Completion.completionPrefix(path, pos)
    val completer = new DeepCompleter(mode, prefix, pos, path)

    val hasBackTick = prefix.headOption.contains('`')

    var extra = List.empty[Completion]

    val completions = path match {
      case Select(qual, _) :: _                              => completer.selectionCompletions(qual)
      case Import(Ident(name), _) :: _
        if name.decode.toString == "$ivy" && dependencyCompleteOpt.nonEmpty =>
        val complete = dependencyCompleteOpt.get
        val (pos, completions) = complete(prefix)
        val input0 = prefix.take(pos)
        extra = extra ::: completions.distinct.toList
          .map(s => maybeBackticked(input0 + s, hasBackTick))
        Map.empty
      case Import(expr, _) :: _                              =>
        completer.directMemberCompletions(expr)
      case (_: untpd.ImportSelector) :: Import(expr, _) :: _ =>
        completer.directMemberCompletions(expr)
      case _                                                 =>
        completer.scopeCompletions ++ {
          if (enableDeep) completer.deepCompletions
          else Nil
        }
    }

    val describedCompletions = extra ++ Completion.describeCompletions(completions).map(backtick)
    val offset = Completion.completionOffset(path)

    (pos.span.start - prefix.length, describedCompletions)
  }

  class DeepCompleter(
    mode: Completion.Mode,
    prefix: String,
    pos: SourcePosition,
    path: List[Tree],
  ) extends Completion.Completer(mode, pos, path, _.startsWith(prefix)):
    private def blacklisted(s: Symbol)(using Context) = {
      val blacklist = Set(
        "scala.Predef.any2stringadd.+",
        "scala.Any.##",
        "java.lang.Object.##",
        "scala.",
        "scala.",
        "scala.",
        "scala.",
        "scala.Predef.StringFormat.formatted",
        "scala.Predef.Ensuring.ensuring",
        "scala.Predef.ArrowAssoc.->",
        "scala.Predef.ArrowAssoc.→",
        "java.lang.Object.synchronized",
        "java.lang.Object.ne",
        "java.lang.Object.eq",
        "java.lang.Object.wait",
        "java.lang.Object.notifyAll",
        "java.lang.Object.notify",
        "java.lang.Object.clone",
        "java.lang.Object.finalize"
      )

      blacklist(s.showFullName) ||
      s.isOneOf(GivenOrImplicit) ||
      // Cache objects, which you should probably never need to
      // access directly, and apart from that have annoyingly long names
      "cache[a-f0-9]{32}".r.findPrefixMatchOf(s.name.decode.toString).isDefined ||
      // s.isDeprecated ||
      s.name.decode.toString == "" ||
      s.name.decode.toString.contains('$')
    }
    def deepCompletions(using Context): Map[Name, Seq[SingleDenotation]] = {

      val blacklistedPackages = Set("shaded")

      def allMembers(s: Symbol) =
        try s.info.allMembers
        catch {
          case _: dotty.tools.dotc.core.TypeError => Nil
        }
      def rec(t: Symbol): Seq[Symbol] =
        if (blacklistedPackages(t.name.toString))
          Nil
        else {
          val children =
            if (t.is(Package) || t.is(PackageVal) || t.is(PackageClass)) {
              allMembers(t).map(_.symbol).filter(!blacklisted(_)).filter(_ != t).flatMap(rec)
            } else Nil

          t +: children.toSeq
        }

      val syms = for {
        member <- allMembers(defn.RootClass).map(_.symbol).filter(!blacklisted(_)).toList
        sym <- rec(member)
      } yield sym

      syms.map(sym => (sym.fullName, List(sym: SingleDenotation))).toMap
    }

}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy