aboutsummaryrefslogtreecommitdiff
path: root/dottydoc/shared/src/main/scala/dotty/tools/dottydoc/model/factories.scala
blob: e8d45384205944cf60327ef41c30fd5934989a0f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
package dotty.tools.dottydoc
package model

import comment._
import dotty.tools.dotc
import dotc.core.Types._
import dotc.core.Contexts.Context
import dotc.core.Symbols.Symbol
import dotty.tools.dotc.core.SymDenotations._
import dotty.tools.dotc.core.Names.TypeName
import dotc.core.{ Flags => DottyFlags }
import dotc.ast.Trees._


object factories {
  import dotty.tools.dotc.ast.tpd._
  import DottyFlags._

  type TypeTree = dotty.tools.dotc.ast.Trees.Tree[Type]

  def flags(t: Tree)(implicit ctx: Context): List[String] =
    (t.symbol.flags & SourceModifierFlags)
      .flagStrings.toList
      .filter(_ != "<trait>")
      .filter(_ != "interface")

  private def pathList(tpe: Type): List[String] = tpe match {
    case t: ThisType =>
      pathList(t.tref)
    case t: NamedType if t.prefix == NoPrefix  && t.name.toString == "<root>" =>
      Nil
    case t: NamedType if t.prefix == NoPrefix =>
      t.name.toString :: Nil
    case t: NamedType =>
      pathList(t.prefix) :+ t.name.toString
  }

  def path(t: Tree)(implicit ctx: Context): List[String] = {
    val ref =
      if (t.symbol.isTerm) t.symbol.termRef
      else t.symbol.typeRef

    pathList(ref)
  }

  private val product = """Product[1-9][0-9]*""".r

  private def cleanTitle(title: String): String = title match {
    // matches Entity.this.Something
    case x if x matches "[^\\[]+\\.this\\..+" => x.split("\\.").last
    // Matches Entity[P, ...]
    case x if x matches "[^\\[]+\\[[^\\]]+\\]" =>
      val Array(tpe, params) = x.dropRight(1).split("\\[")
      s"""$tpe[${params.split(",").map(x => cleanTitle(x.trim)).mkString(", ")}]"""
    case _ => title
  }

  private def cleanQuery(query: String): String = query match {
    case x if x matches "[^\\[]+\\[[^\\]]+\\]" => x.takeWhile(_ != '[')
    case _ => query
  }

  def returnType(t: TypeTree)(implicit ctx: Context): Reference = {
    def typeRef(name: String, params: List[MaterializableLink]) =
      TypeReference(name, UnsetLink(Text(name), name), params)

    def expandTpe(t: Type, params: List[MaterializableLink] = Nil): Reference = t match {
      case ref @ RefinedType(parent, rn) => {
        val paramName = ref.refinedInfo match {
          case ta: TypeAlias if ta.alias.isInstanceOf[NamedType] =>
            ta.alias.asInstanceOf[NamedType].name.decode.toString
          case _ =>
            rn.decode.toString.split("\\$").last
        }
        val param = UnsetLink(Text(paramName), paramName)
        expandTpe(parent, param :: params)
      }
      case TypeRef(_, name) =>
        typeRef(name.decode.toString, params)
      case OrType(left, right) =>
        OrTypeReference(expandTpe(left), expandTpe(right))
      case AndType(left, right) =>
        AndTypeReference(expandTpe(left), expandTpe(right))
      case AnnotatedType(tpe, _) =>
        expandTpe(tpe)
      case ExprType(tpe) =>
        expandTpe(tpe)
      case c: ConstantType =>
        ConstantReference(c.show)
      case tt: ThisType =>
        expandTpe(tt.underlying)
    }

    expandTpe(t.tpe)
  }

  def typeParams(t: Tree)(implicit ctx: Context): List[String] = t match {
    case t: DefDef =>
      def variance(s: Symbol) =
        if (s is Covariant) "+"
        else if (s is Contravariant) "-"
        else ""
      t.tparams.map(p => variance(p.symbol) + p.show)
    case t: TypeDef if t.rhs.isInstanceOf[Template] =>
      // Get the names from the constructor method `DefDef`
      typeParams(t.rhs.asInstanceOf[Template].constr)
  }

  def paramLists(t: DefDef)(implicit ctx: Context): List[List[NamedReference]] = {
    def getParams(xs: List[ValDef]): List[NamedReference] =
      xs.map(vd => NamedReference(vd.name.decode.toString, returnType(vd.tpt)))

    t.vparamss.map(getParams)
  }

  def superTypes(t: Tree)(implicit ctx: Context): List[MaterializableLink] = t.symbol.denot match {
    case cd: ClassDenotation =>
      def isJavaLangObject(prefix: Type): Boolean =
        prefix match {
          case TypeRef(ThisType(TypeRef(NoPrefix, outerName)), innerName) =>
            outerName.toString == "lang" && innerName.toString == "Object"
          case _ => false
        }

      def isProductWithArity(prefix: Type): Boolean = prefix match {
        case TypeRef(TermRef(TermRef(NoPrefix, root), scala), prod) =>
          root.toString == "_root_" &&
          scala.toString == "scala" &&
          product.findFirstIn(prod.toString).isDefined
        case _ => false
      }

      cd.classParents.collect {
        case t: TypeRef if !isJavaLangObject(t) && !isProductWithArity(t) =>
          UnsetLink(Text(t.name.toString), pathList(t).mkString("."))
      }
    case _ => Nil
  }
}