aboutsummaryrefslogblamecommitdiff
path: root/doc-tool/src/dotty/tools/dottydoc/staticsite/Page.scala
blob: b637caf384bb39ea771be213254c764e2225f7d8 (plain) (tree)
1
2
3
4
5
6
7
8
9



                   
                                    



                                                                           
 

                    
                                               
 


                                                                         

                                          
                                   


                                 
                                   



                                   
                      



                                   



























                                                       
                                                                                 
                                       

                                                                          
                                                            
                           
 

                         
                        
                  




                                                                   

                     
            
     
 













                                                                  
                                                          
                                           
                                                                        
   




                                                                            
                                                                              


                
                                                  



                                        

 
                                                                                                                          


                                     
                                                                                                                                                          
                                     
 
                                               
                      
                                                                    
                           
                                         
                        
                                    

                        
                 

   
package dotty.tools
package dottydoc
package staticsite

import dotc.config.Printers.dottydoc

import com.vladsch.flexmark.html.HtmlRenderer
import com.vladsch.flexmark.parser.Parser
import com.vladsch.flexmark.ext.front.matter.AbstractYamlFrontMatterVisitor

import model.Package

import java.util.{ Map => JMap, List => JList }

case class IllegalFrontMatter(message: String) extends Exception(message)

trait Page {
  import scala.collection.JavaConverters._

  def includes: Map[String, String]
  def pageContent: String
  def params: Map[String, AnyRef]

  def yaml: Map[String, AnyRef] = {
    if (_yaml eq null) initFields()
    _yaml
  }

  def html: String = {
    if (_html eq null) initFields()
    _html
  }

  def firstParagraph: String = {
    if (_html eq null) initFields()

    val sb = new StringBuilder
    var pos = 0
    // to handle nested paragraphs in non markdown code
    var open = 0

    while (pos < _html.length - 4) {
      val str = _html.substring(pos, pos + 4)
      val lstr = str.toLowerCase
      sb append str.head

      pos += 1
      if (lstr.contains("<p>"))
        open += 1
      else if (lstr == "</p>") {
        open -= 1
        if (open == 0) {
          pos = Int.MaxValue
          sb append "/p>"
        }
      }
    }

    sb.toString
  }

  protected[this] var _yaml: Map[String, AnyRef /* String | JList[String] */] = _
  protected[this] var _html: String = _
  protected[this] def initFields() = {
    val md = Parser.builder(Site.markdownOptions).build.parse(pageContent)
    val yamlCollector = new AbstractYamlFrontMatterVisitor()
    yamlCollector.visit(md)

    _yaml = updatedYaml {
      yamlCollector
      .getData().asScala
      .mapValues {
        case xs if xs.size == 1 =>
          val str = xs.get(0)
          if (str.length > 0 && str.head == '"' && str.last == '"')
            str.substring(1, str.length - 1)
          else str
        case xs => xs
      }
      .toMap
    }

    // YAML must start with "---" and end in either "---" or "..."
    val withoutYaml =
      if (pageContent.startsWith("---\n")) {
        val str =
          pageContent.lines
          .drop(1)
          .dropWhile(line => line != "---" && line != "...")
          .drop(1).mkString("\n")

        if (str.isEmpty) throw IllegalFrontMatter(pageContent)
        else str
      }
      else pageContent

    // make accessible via "{{ page.title }}" in templates
    val page = Map("page" ->  _yaml.asJava)
    _html = LiquidTemplate(withoutYaml).render(params ++ page, includes)
  }

  /** Takes "page" from `params` map in case this is a second expansion, and
    * removes "layout" from the parameters if it exists. We don't want to
    * preserve the layout from the previously expanded template
    */
  private def updatedYaml(newYaml: Map[String, AnyRef]): Map[String, AnyRef] =
    params
    .get("page")
    .flatMap {
      case page: Map[String, AnyRef] @unchecked =>
        Some(page - "layout" ++ newYaml)
      case _ => None
    }
    .getOrElse(newYaml)
}

class HtmlPage(fileContents: => String, val params: Map[String, AnyRef], val includes: Map[String, String]) extends Page {
  lazy val pageContent = fileContents
}

class MarkdownPage(fileContents: => String, val params: Map[String, AnyRef], val includes: Map[String, String], docs: Map[String, Package]) extends Page {
  lazy val pageContent = fileContents

  override protected[this] def initFields() = {
    super.initFields()
    val md = Parser.builder(Site.markdownOptions).build.parse(_html)
    // fix markdown linking
    MarkdownLinkVisitor(md, docs, params)
    _html = HtmlRenderer
      .builder(Site.markdownOptions)
      .escapeHtml(false)
      .build()
      .render(md)
  }
}