summaryrefslogtreecommitdiff
path: root/src/scaladoc/scala/tools/nsc/doc/DocParser.scala
blob: f03b848af6188d2dc027f1d36ad2b3d4437bd720 (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
/* NSC -- new Scala compiler
 * Copyright 2005-2013 LAMP/EPFL
 * @author  Paul Phillips
 */

package scala.tools
package nsc
package doc

import reporters._
import scala.reflect.internal.util._
import DocParser.Parsed

/** A very minimal global customized for extracting `DocDefs`.  It stops
 *  right after parsing so it can read `DocDefs` from source code which would
 *  otherwise cause the compiler to go haywire.
 */
class DocParser(settings: nsc.Settings, reporter: Reporter) extends Global(settings, reporter) with ScaladocGlobalTrait {
  def this(settings: Settings) = this(settings, new ConsoleReporter(settings))
  def this() = this(new Settings(Console println _))

  // the usual global initialization
  locally { new Run() }

  override def forScaladoc = true
  override protected def computeInternalPhases() {
    phasesSet += syntaxAnalyzer
  }

  /** Returns a list of `DocParser.Parseds`, which hold the DocDefs found
   *  in the given code along with the surrounding trees.
   */
  def docDefs(code: String) = {
    def loop(enclosing: List[Tree], tree: Tree): List[Parsed] = tree match {
      case x: PackageDef => x.stats flatMap (t => loop(enclosing :+ x, t))
      case x: DocDef     => new Parsed(enclosing, x) :: loop(enclosing :+ x.definition, x.definition)
      case x             => x.children flatMap (t => loop(enclosing, t))
    }
    loop(Nil, docUnit(code))
  }

  /** A compilation unit containing parsed source.
   */
  def docUnit(code: String) = {
    val unit    = new CompilationUnit(new BatchSourceFile("<console>", code))
    val scanner = newUnitParser(unit)

    scanner.compilationUnit()
  }
}

/** Since the DocParser's whole reason for existing involves trashing a
 *  global, it is designed to bottle up general `Global#Tree` types rather
 *  than path dependent ones.  The recipient will have to deal.
 */
object DocParser {
  type Tree    = Global#Tree
  type DefTree = Global#DefTree
  type DocDef  = Global#DocDef
  type Name    = Global#Name

  class Parsed(val enclosing: List[Tree], val docDef: DocDef) {
    def nameChain: List[Name] = (enclosing :+ docDef.definition) collect { case x: DefTree => x.name }
    def raw: String           = docDef.comment.raw

    override def toString = (
      nameChain.init.map(x => if (x.isTypeName) x + "#" else x + ".").mkString + nameChain.last
    )
  }
}