in scala/scala-impl/src/org/jetbrains/plugins/scala/lang/completion/ScalaSmartCompletionContributor.scala [330:609]
private def superParentPattern(clazz: Class[_ <: ScalaPsiElement]) =
identifierWithParentsPattern(classOf[ScReferenceExpression], clazz) ||
identifierWithParentsPattern(classOf[ScReferenceExpression], classOf[ScReferenceExpression], clazz)
private def acceptTypes(types: Iterable[ScType], ref: ScReferenceExpression)
(implicit parameters: CompletionParameters, result: CompletionResultSet, place: PsiElement): Unit =
acceptTypes(
types,
ref.getVariants,
result,
parameters.getInvocationCount > 1,
ScalaCompletionUtil.hasNoQualifier(ref),
parameters.getOriginalPosition,
parameters.getCompletionType == CompletionType.SMART
)
private def acceptTypes(typez: Iterable[ScType], variants: Array[Object],
result: CompletionResultSet,
secondCompletion: Boolean,
completeThis: Boolean,
originalPlace: PsiElement,
isSmart: Boolean)
(implicit place: PsiElement): Unit = {
implicit val projectContext: Project = place.getProject
implicit val context: Context = Context(place)
if (typez.isEmpty || typez.forall(_ == Nothing)) return
def applyVariant(variant: Object, checkForSecondCompletion: Boolean = false): Unit = {
def handleVariant(scalaLookupItem: ScalaLookupItem, chainVariant: Boolean = false): Unit = {
val elemToAdd = variant.asInstanceOf[LookupElement]
if (isAccessible(scalaLookupItem) && !scalaLookupItem.isNamedParameterOrAssignment) {
def checkType(_tp: ScType, _subst: ScSubstitutor, chainCompletion: Boolean, etaExpanded: Boolean = false): Boolean = {
val tp = _subst(_tp)
var elementAdded = false
val scType = scalaLookupItem.substitutor(tp)
if (!scType.equiv(Nothing) && typez.exists(scType conforms _)) {
elementAdded = true
if (etaExpanded) scalaLookupItem.etaExpanded = true
result.addElement(PrioritizedLookupElement.withPriority(elemToAdd, 1))
} else {
typez.foreach {
case ParameterizedType(tpe, Seq(arg)) if !elementAdded =>
tpe.extractClass match {
case Some(clazz) if clazz.qualifiedName == "scala.Option" || clazz.qualifiedName == "scala.Some" =>
if (!scType.equiv(Nothing) && scType.conforms(arg)) {
scalaLookupItem.someSmartCompletion = true
if (etaExpanded) scalaLookupItem.etaExpanded = true
result.addElement(elemToAdd)
elementAdded = true
}
case _ =>
}
case _ =>
}
}
if (!elementAdded && chainCompletion && secondCompletion) {
val processor = new CompletionProcessor(StdKinds.refExprLastRef, place) {
override protected def postProcess(result: ScalaResolveResult): Unit = {
if (!result.isNamedParameter) {
val variant = new ScalaChainLookupElement(
result.createLookupElement(),
scalaLookupItem
)
applyVariant(variant)
}
}
}
processor.processType(scalaLookupItem.substitutor(_tp), place)
processor.candidatesS
}
elementAdded
}
def checkTyped(typed: PsiNamedElement with Typeable): Unit = {
if (!PsiTreeUtil.isContextAncestor(typed.nameContext, place, false) &&
(originalPlace == null || !PsiTreeUtil.isContextAncestor(typed.nameContext, originalPlace, false)))
for (tt <- typed.`type`()) checkType(tt, ScSubstitutor.empty, checkForSecondCompletion)
}
scalaLookupItem.getPsiElement match {
case clazz: PsiClass if isExcluded(clazz) =>
case fun: ScSyntheticFunction =>
val second = checkForSecondCompletion && fun.paramClauses.flatten.isEmpty
checkType(fun.retType, ScSubstitutor.empty, second)
case fun: ScFunction =>
if (fun.containingClass != null && fun.containingClass.qualifiedName == "scala.Predef") {
fun.name match {
case "implicitly" | "identity" | "locally" => return
case _ =>
}
}
val infer = if (chainVariant) ScSubstitutor.empty else ScalaPsiUtil.undefineMethodTypeParams(fun)
val second = checkForSecondCompletion &&
fun.paramClauses.clauses.filterNot(_.isImplicit).forall(_.parameters.isEmpty)
val added = fun.returnType match {
case Right(tp) => checkType(tp, infer, second)
case _ => false
}
if (!added) {
fun.`type`() match {
case Right(tp) => checkType(tp, infer, second, etaExpanded = true)
case _ =>
}
}
case method: PsiMethod =>
val second = checkForSecondCompletion && method.getParameterList.getParametersCount == 0
val infer = if (chainVariant) ScSubstitutor.empty else ScalaPsiUtil.undefineMethodTypeParams(method)
checkType(method.getReturnType.toScType(), infer, second)
case typed: ScTypedDefinition => checkTyped(typed)
case typed: ScEnumCase => checkTyped(typed)
// TODO: java classes?
case typed: ScTypeDefinition => checkTyped(typed)
case f: PsiField =>
checkType(f.getType.toScType(), ScSubstitutor.empty, checkForSecondCompletion)
case _ =>
}
}
}
variant match {
case el: ScalaLookupItem => handleVariant(el)
case ch: ScalaChainLookupElement => handleVariant(ch.getDelegate, chainVariant = true)
case _ =>
}
}
place.getContext match {
case ref: ScReferenceExpression if ref.smartQualifier.isEmpty =>
//enum and factory methods
val iterator = typez.iterator
while (iterator.hasNext) {
val tp = iterator.next() match {
// SCL-21582: e.g.: `A1_ >: WeekDayScala3` -> `WeekDayScala3`
case t: ScAbstractType => t.removeAbstracts
case t => t
}
// todo unify with SbtCompletionContributor.collectAndApplyVariants
def checkObject(containingClass: ScObject): Unit =
if (isAccessible(containingClass) && ScalaPsiUtil.hasStablePath(containingClass)) {
containingClass.membersWithSynthetic.flatMap {
case function: ScFunction => Seq(function)
case v: ScValueOrVariable => v.declaredElements
case obj: ScObject => Seq(obj)
case _ => Seq.empty
}.map {
createLookupElementWithPrefix(_, containingClass)
}.foreach {
applyVariant(_)
}
}
def checkTypeProjection(tp: ScType): Unit = {
tp match {
case ScProjectionType(proj, _: ScTypeAlias | _: ScClass | _: ScTrait) =>
proj.extractClass match {
case Some(o: ScObject) => checkObject(o)
case _ =>
}
case _ =>
}
}
@tailrec
def checkType(tp: ScType): Unit = {
if (!isSmart) { // SCL-19749
tp.extractClass match {
case Some(scEnum: ScEnum) =>
if (isAccessible(scEnum)) {
val cases = scEnum.cases
// cases that are already in scope will be added by ScalaBasicCompletionContributor (SCL-22855)
val casesFiltered = cases.filterNot(isAccessibleWithoutExtraImports(_))
casesFiltered.foreach { enumCase =>
applyVariant(createLookupElement(enumCase, scEnum))
}
}
case Some(javaEnum@JavaEnum(fields)) if isAccessible(javaEnum) =>
val fieldsFiltered = fields.filterNot(isAccessibleWithoutExtraImports(_))
fieldsFiltered.foreach(field => applyVariant(createLookupElementWithPrefix(field, javaEnum)))
case cls =>
val valueType = tp.extractDesignatorSingleton.getOrElse(tp)
valueType match {
case ScProjectionType(DesignatorOwner(enumeration@ScalaEnumeration(vals)), _) if isAccessible(enumeration) =>
val applicableVals = vals.filter(v => isAccessible(v) && v.`type`().exists(_.conforms(valueType)))
val fields = applicableVals.flatMap(_.declaredElements)
val fieldsFiltered = fields.filterNot(isAccessibleWithoutExtraImports(_))
fieldsFiltered.foreach(field => applyVariant(createLookupElementWithPrefix(field, enumeration)))
case _ =>
cls match {
// SCL-21582
case Some(sealedClass@SealedClassInheritors(cases)) if isAccessible(sealedClass) =>
val casesFiltered = cases.filterNot(isAccessibleWithoutExtraImports(_))
casesFiltered.foreach { c =>
applyVariant(createLookupElement(c, sealedClass))
}
case _ =>
}
}
}
} else tp.extractClass match {
case Some(c: ScClass) if c.qualifiedName == "scala.Option" || c.qualifiedName == "scala.Some" =>
tp match {
case ParameterizedType(_, Seq(scType)) => checkType(scType)
case _ =>
}
case Some(_: ScObject) => //do nothing
case Some(clazz: ScTypeDefinition) =>
checkTypeProjection(tp)
ScalaPsiUtil.getCompanionModule(clazz) match {
case Some(o: ScObject) => checkObject(o)
case _ => //do nothing
}
case Some(containingClass: PsiClass) if isAccessible(containingClass) =>
// todo unify with SbtCompletionContributor
for {
member <- containingClass.getAllMethods ++ containingClass.getFields
if member.hasModifierProperty(PsiModifier.STATIC) &&
isAccessible(member)
variant = createLookupElementWithPrefix(member, containingClass)
} applyVariant(variant)
case _ => checkTypeProjection(tp)
}
}
checkType(tp)
}
if (isSmart) {
variants.foreach(applyVariant(_, checkForSecondCompletion = true))
if (typez.exists(_.equiv(Boolean))) {
ScalaKeywordLookupItem.addFor(result, ScalaKeyword.FALSE, ScalaKeyword.TRUE)
}
if (completeThis) {
var parent = place
var foundClazz = false
while (parent != null) {
parent match {
case _: ScNewTemplateDefinition if foundClazz => //do nothing, impossible to invoke
case t: ScTemplateDefinition =>
t.getTypeWithProjections(thisProjections = true) match {
case Right(scType) =>
val lookupString = (if (foundClazz) t.name + "." else "") + "this"
val el = new ScalaLookupItem(t, lookupString)
if (!scType.equiv(Nothing) && typez.exists(scType conforms _)) {
if (!foundClazz) el.bold = true
result.addElement(el)
} else {
var elementAdded = false
typez.foreach {
case ParameterizedType(tp, Seq(arg)) if !elementAdded =>
tp.extractClass match {
case Some(clazz) if clazz.qualifiedName == "scala.Option" || clazz.qualifiedName == "scala.Some" =>
if (!scType.equiv(Nothing) && scType.conforms(arg)) {
el.someSmartCompletion = true
result.addElement(el)
elementAdded = true
}
case _ =>
}
case _ =>
}
}
case _ =>
}
foundClazz = true
case _ =>
}
parent = parent.getContext
}
}
}
case _ =>
if (isSmart) variants.foreach(applyVariant(_, checkForSecondCompletion = true))
}
}