|
| 1 | +package dotty.tools.pc.completions |
| 2 | + |
| 3 | +import scala.meta.internal.metals.Fuzzy |
| 4 | +import dotty.tools.pc.utils.InteractiveEnrichments.* |
| 5 | +import dotty.tools.pc.completions.CompletionValue.SingletonValue |
| 6 | + |
| 7 | +import dotty.tools.dotc.ast.tpd.* |
| 8 | +import dotty.tools.dotc.core.Constants.Constant |
| 9 | +import dotty.tools.dotc.core.Contexts.Context |
| 10 | +import dotty.tools.dotc.core.Flags |
| 11 | +import dotty.tools.dotc.core.StdNames |
| 12 | +import dotty.tools.dotc.core.Symbols |
| 13 | +import dotty.tools.dotc.core.Types.AndType |
| 14 | +import dotty.tools.dotc.core.Types.AppliedType |
| 15 | +import dotty.tools.dotc.core.Types.ConstantType |
| 16 | +import dotty.tools.dotc.core.Types.OrType |
| 17 | +import dotty.tools.dotc.core.Types.TermRef |
| 18 | +import dotty.tools.dotc.core.Types.Type |
| 19 | +import dotty.tools.dotc.core.Types.TypeRef |
| 20 | +import dotty.tools.dotc.util.Spans.Span |
| 21 | +import dotty.tools.dotc.core.Symbols.defn |
| 22 | + |
| 23 | +object SingletonCompletions: |
| 24 | + def contribute( |
| 25 | + path: List[Tree], |
| 26 | + tpe0: Type, |
| 27 | + completionPos: CompletionPos |
| 28 | + )(using ctx: Context): List[CompletionValue] = |
| 29 | + for { |
| 30 | + (name, span) <- |
| 31 | + path match |
| 32 | + case (i @ Ident(name)) :: _ => List(name.toString() -> i.span) |
| 33 | + case (l @ Literal(const)) :: _ => List(const.show -> l.span) |
| 34 | + case _ => Nil |
| 35 | + query = name.replace(Cursor.value, "").nn |
| 36 | + tpe = tpe0 match |
| 37 | + // for Tuple 2 we want to suggest first arg completion |
| 38 | + case AppliedType(t: TypeRef, args) if t.classSymbol == Symbols.defn.Tuple2 && args.nonEmpty => |
| 39 | + args.head |
| 40 | + case t => t |
| 41 | + singletonValues = collectSingletons(tpe).map(_.show) |
| 42 | + range = completionPos.originalCursorPosition.withStart(span.start).withEnd(span.start + query.length).toLsp |
| 43 | + value <- singletonValues.collect { |
| 44 | + case name if Fuzzy.matches(query, name) => |
| 45 | + SingletonValue(name, tpe, Some(range)) |
| 46 | + } |
| 47 | + } yield value |
| 48 | + |
| 49 | + private def collectSingletons(tpe: Type)(using Context): List[Constant] = |
| 50 | + tpe.deepDealias match |
| 51 | + case ConstantType(value) => List(value) |
| 52 | + case OrType(tpe1, tpe2) => |
| 53 | + collectSingletons(tpe1) ++ collectSingletons(tpe2) |
| 54 | + case AndType(tpe1, tpe2) => |
| 55 | + collectSingletons(tpe1).intersect(collectSingletons(tpe2)) |
| 56 | + case _ => Nil |
| 57 | + |
| 58 | +object InterCompletionType: |
| 59 | + def inferType(path: List[Tree])(using Context): Option[Type] = |
| 60 | + path match |
| 61 | + case (lit: Literal) :: Select(Literal(_), _) :: Apply(Select(Literal(_), _), List(s: Select)) :: rest if s.symbol == defn.Predef_undefined => |
| 62 | + inferType(rest, lit.span) |
| 63 | + case ident :: rest => inferType(rest, ident.span) |
| 64 | + case _ => None |
| 65 | + |
| 66 | + def inferType(path: List[Tree], span: Span)(using Context): Option[Type] = |
| 67 | + path match |
| 68 | + case Apply(head, List(p : Select)) :: rest if p.name == StdNames.nme.??? && p.qualifier.symbol.name == StdNames.nme.Predef && p.span.isSynthetic => |
| 69 | + inferType(rest, span) |
| 70 | + case Block(_, expr) :: rest if expr.span.contains(span) => |
| 71 | + inferType(rest, span) |
| 72 | + case If(cond, _, _) :: rest if !cond.span.contains(span) => |
| 73 | + inferType(rest, span) |
| 74 | + case Typed(expr, tpt) :: _ if expr.span.contains(span) && !tpt.tpe.isErroneous => Some(tpt.tpe) |
| 75 | + case Block(_, expr) :: rest if expr.span.contains(span) => |
| 76 | + inferType(rest, span) |
| 77 | + case Bind(_, body) :: rest if body.span.contains(span) => inferType(rest, span) |
| 78 | + case Alternative(_) :: rest => inferType(rest, span) |
| 79 | + case Try(block, _, _) :: rest if block.span.contains(span) => inferType(rest, span) |
| 80 | + case CaseDef(_, _, body) :: Try(_, cases, _) :: rest if body.span.contains(span) && cases.exists(_.span.contains(span)) => inferType(rest, span) |
| 81 | + case If(cond, _, _) :: rest if !cond.span.contains(span) => inferType(rest, span) |
| 82 | + case CaseDef(_, _, body) :: Match(_, cases) :: rest if body.span.contains(span) && cases.exists(_.span.contains(span)) => |
| 83 | + inferType(rest, span) |
| 84 | + case NamedArg(_, arg) :: rest if arg.span.contains(span) => inferType(rest, span) |
| 85 | + // x match |
| 86 | + // case @@ |
| 87 | + case CaseDef(pat, _, _) :: Match(sel, cases) :: rest if pat.span.contains(span) && cases.exists(_.span.contains(span)) && !sel.tpe.isErroneous => |
| 88 | + sel.tpe match |
| 89 | + case tpe: TermRef => Some(tpe.symbol.info).filterNot(_.isErroneous) |
| 90 | + case tpe => Some(tpe) |
| 91 | + // List(@@) |
| 92 | + case SeqLiteral(_, tpe) :: _ if !tpe.tpe.isErroneous => |
| 93 | + Some(tpe.tpe) |
| 94 | + // val _: T = @@ |
| 95 | + // def _: T = @@ |
| 96 | + case (defn: ValOrDefDef) :: rest if !defn.tpt.tpe.isErroneous => Some(defn.tpt.tpe) |
| 97 | + // f(@@) |
| 98 | + case (app: Apply) :: rest => |
| 99 | + val param = |
| 100 | + for { |
| 101 | + ind <- app.args.zipWithIndex.collectFirst { |
| 102 | + case (arg, id) if arg.span.contains(span) => id |
| 103 | + } |
| 104 | + params <- app.symbol.paramSymss.find(!_.exists(_.isTypeParam)) |
| 105 | + param <- params.get(ind) |
| 106 | + } yield param.info |
| 107 | + param match |
| 108 | + // def f[T](a: T): T = ??? |
| 109 | + // f[Int](@@) |
| 110 | + // val _: Int = f(@@) |
| 111 | + case Some(t : TypeRef) if t.symbol.is(Flags.TypeParam) => |
| 112 | + for { |
| 113 | + (typeParams, args) <- |
| 114 | + app match |
| 115 | + case Apply(TypeApply(fun, args), _) => |
| 116 | + val typeParams = fun.symbol.paramSymss.headOption.filter(_.forall(_.isTypeParam)) |
| 117 | + typeParams.map((_, args.map(_.tpe))) |
| 118 | + // val f: (j: "a") => Int |
| 119 | + // f(@@) |
| 120 | + case Apply(Select(v, StdNames.nme.apply), _) => |
| 121 | + v.symbol.info match |
| 122 | + case AppliedType(des, args) => |
| 123 | + Some((des.typeSymbol.typeParams, args)) |
| 124 | + case _ => None |
| 125 | + case _ => None |
| 126 | + ind = typeParams.indexOf(t.symbol) |
| 127 | + tpe <- args.get(ind) |
| 128 | + if !tpe.isErroneous |
| 129 | + } yield tpe |
| 130 | + case Some(tpe) => Some(tpe) |
| 131 | + case _ => None |
| 132 | + case _ => None |
| 133 | + |
0 commit comments