All Downloads are FREE. Search and download functionalities are using the official Maven repository.

dotty.tastydoc.DocPrinter.scala Maven / Gradle / Ivy

The newest version!
package dotty.tastydoc

import representations._
import references._
import comment.Comment

import scala.annotation.tailrec

import java.io._

class DocPrinter(mutablePackagesMap: scala.collection.mutable.HashMap[String, EmulatedPackageRepresentation], userDocSyntax: String, packagesToLink: List[String]){

  val packagesMap = mutablePackagesMap.toMap

  private def htmlPreCode(content: String, language: String = ""): String = {
    "
" + content + "
" } private def makeLink(label: String, link: String, hasOwnFile: Boolean, declarationPath: List[String]): String = { val labelName = label.stripSuffix("$") val packageFormLink = link.replaceFirst("/", "").replaceAll("/", ".") if(packagesToLink.exists(packageFormLink.matches(_))){ @tailrec def ascendPath(path: List[String], link: List[String]): String = path match { case x::xs if link.nonEmpty && link.head == x => ascendPath(xs, link.tail) case _ => (if(path.isEmpty) "." else path.map(_ => "..").mkString("/")) + (if(link.isEmpty) "" else link.mkString("/", "/", "")) } val relativeLink = { if(link == ""){ if(hasOwnFile){ if(declarationPath.isEmpty){ "." }else { declarationPath.map(_ => "..").mkString("/") } }else{ "" } }else{ ascendPath(declarationPath, link.split("/").toList.tail) } } if(hasOwnFile){ "" + labelName + "" } else if(relativeLink == "") { labelName } else if(relativeLink == "."){ "" + labelName + "" } else{ "" + labelName + "" } }else{ labelName } } private def formatReferences(reference: Reference, declarationPath: List[String]) : String = reference match { case CompanionReference(label, link, kind) => makeLink(label, link, true, declarationPath) case TypeReference(label, link, typeParams, hasOwnFile) => if(typeParams.isEmpty){ makeLink(label, link, hasOwnFile, declarationPath) }else{ makeLink(label, link, hasOwnFile, declarationPath) + typeParams.map(formatReferences(_, declarationPath)).mkString("[", ", ", "]") } case OrTypeReference(left, right) => formatReferences(left, declarationPath) + " | " + formatReferences(right, declarationPath) case AndTypeReference(left, right) => formatReferences(left, declarationPath) + " & " + formatReferences(right, declarationPath) case FunctionReference(args, returnValue, isImplicit) => args.map(formatReferences(_, declarationPath)).mkString("(", ", ", ") => ") + formatReferences(returnValue, declarationPath) case TupleReference(args) => args.map(formatReferences(_, declarationPath)).mkString("(", ", ", ")") case BoundsReference(low, high) => formatReferences(low, declarationPath) + " <: " + formatReferences(high, declarationPath) case ByNameReference(ref) => "=> " + formatReferences(ref, declarationPath) case ConstantReference(label) => label case NamedReference(name, ref, isRepeated) => name + ": " + formatReferences(ref, declarationPath) + (if(isRepeated) "*" else "") case RefinedReference(parent, ls) => formatReferences(parent, declarationPath) + ls.map((kind, name, tpe) => kind + " " + name + ": " + formatReferences(tpe, declarationPath)).mkString("{ ", "; ", " }") case EmptyReference => "" } private def formatParamList(paramList: ParamList, declarationPath: List[String]) : String = paramList.list.map(x => formatReferences(x, declarationPath)).mkString( "(" + (if(paramList.isImplicit) "implicit " else ""), ", ", ")" ) private def formatModifiers(modifiers: List[String], privateWithin: Option[Reference], protectedWithin: Option[Reference], annotations: List[TypeReference], declarationPath: List[String]): String = { val hasInlineAnnot = annotations.contains(TypeReference("forceInline", "/scala", Nil, true)) val filteredModifiers = modifiers.filter(x => x != "private" && x != "protected" && (!hasInlineAnnot || x != "inline")) (privateWithin match { case Some(r) => formatReferences(r, declarationPath).mkString("private[", "", "] ") case None if modifiers.contains("private") => "private " case None => "" }) + (protectedWithin match { case Some(r) => formatReferences(r, declarationPath).mkString("private[", "", "] ") case None if modifiers.contains("protected") => "protected " case None => "" }) + (if(filteredModifiers.nonEmpty) filteredModifiers.mkString("", " ", " ") else "") } private def formatComments(comment: (Map[String, EmulatedPackageRepresentation], String) => Option[Comment]) : String = comment(packagesMap, userDocSyntax) match { case Some(c) => def removeLineEnds(str: String): String = { if(str.isEmpty){ str }else{ str.last match { case '\n' => removeLineEnds(str.stripLineEnd) case _ => str + "\n\n" } } } removeLineEnds(c.body) + (if(c.authors.nonEmpty) Md.bold(Md.italics("authors")) + " " + c.authors.mkString(", ") + "\n" else "") + (if(c.see.nonEmpty) Md.bold(Md.italics("see")) + " " + c.see.mkString(", ") + "\n" else "") + (if(c.result.isDefined) Md.bold(Md.italics("return")) + " " + c.result.get + "\n" else "") + (if(c.throws.nonEmpty) c.throws.map { case (x, y) => Md.bold(Md.italics(x)) + " " + y }.mkString("\n") else "") + (if(c.valueParams.nonEmpty) c.valueParams.map { case (x, y) => Md.bold(Md.italics(x)) + " " + y }.mkString("\n") + "\n" else "") + (if(c.typeParams.nonEmpty) c.typeParams.map { case (x, y) => Md.bold(Md.italics(x)) + " " + y }.mkString("\n") + "\n" else "") + (if(c.version.isDefined) Md.bold(Md.italics("version")) + " " + c.version.get + "\n" else "") + (if(c.since.isDefined) Md.bold(Md.italics("since")) + " " + c.since.get + "\n" else "") + (if(c.todo.nonEmpty) Md.bold(Md.italics("TODO")) + " " + c.todo.mkString(", ") + "\n" else "") + (if(c.deprecated.isDefined) Md.bold(Md.italics("deprecated")) + " " + c.deprecated.get + "\n" else "") + (if(c.note.nonEmpty) Md.bold(Md.italics("Note")) + " " + c.note.mkString("\n") + "\n"else "") + (if(c.example.nonEmpty) Md.bold(Md.italics("Example")) + " " + c.example.mkString("\n") + "\n" else "") + (if(c.constructor.isDefined) Md.bold(Md.italics("Constructor")) + " " + c.constructor.get + "\n" else "") + (if(c.group.isDefined) Md.bold(Md.italics("Group")) + " " + c.group.get + "\n" else "") + (if(c.groupDesc.nonEmpty) c.groupDesc.map { case (x, y) => Md.bold(Md.italics(x)) + " " + y }.mkString("\n") else "") + (if(c.groupNames.nonEmpty) c.groupNames.map { case (x, y) => Md.bold(Md.italics(x)) + " " + y }.mkString("\n") + "\n" else "") + (if(c.groupPrio.nonEmpty) c.groupPrio.map { case (x, y) => Md.bold(Md.italics(x)) + " " + y }.mkString("\n") + "\n" else "") + (if(c.hideImplicitConversions.nonEmpty) Md.bold(Md.italics("Hide Implicit Conversions")) + " " + c.hideImplicitConversions.mkString(", ") + "\n" else "") case None => "" } private def formatAnnotations(annotations: List[TypeReference], declarationPath: List[String]): String = { val str = annotations.map("@" + formatReferences(_, declarationPath)).mkString(" ") if(str.isEmpty) str else str + " " } private def formatSimplifiedClassRepresentation(representation: ClassRepresentation, declarationPath: List[String]): String = { def formatSimplifiedSignature(): String = { htmlPreCode( formatAnnotations(representation.annotations, declarationPath) + formatModifiers(representation.modifiers, representation.privateWithin, representation.protectedWithin, representation.annotations, declarationPath) + representation.kind + " " + makeLink(representation.name, representation.path.mkString("/", "/", ""), true, declarationPath) , "scala") + "\n" } formatSimplifiedSignature() + formatComments(representation.comments) } private def formatClassRepresentation(representation: ClassRepresentation, declarationPath: List[String]): String = { def formatCompanion(): String = representation.companion match { case Some(ref@CompanionReference(_, _, kind)) => Md.header2("Companion " + kind + " " + formatReferences(ref, declarationPath) ) + "\n" case _ => "" } def formatSignature(): String = { htmlPreCode(formatModifiers(representation.modifiers, representation.privateWithin, representation.protectedWithin, representation.annotations, representation.path) + representation.kind + " " + (if(representation.isObject) representation.name.stripSuffix("$") else representation.name) + (if(representation.typeParams.nonEmpty) representation.typeParams.mkString("[", ", ", "]") else "") + (if(representation.parents.nonEmpty) " extends " + formatReferences(representation.parents.head, representation.path) + representation.parents.tail.map(" with " + formatReferences(_, representation.path)).mkString("") else "") , "scala") + "\n" } def formatClassAnnotations(): String = { if(representation.annotations.isEmpty){ "" }else{ Md.header2("Annotations:") + formatAnnotations(representation.annotations, declarationPath) + "\n" } } def formatKnownSubclasses(): String = { if(representation.knownSubclasses.isEmpty){ "" }else{ Md.header2("Known subclasses:") + representation.knownSubclasses.map(formatReferences(_, declarationPath)).mkString(", ") + "\n" } } def formatConstructors(): String = { if(representation.constructors.isEmpty || representation.isObject || (representation.constructors.size == 1 && representation.constructors.head.paramLists.size == 1 && representation.constructors.head.paramLists.head.list == Nil)){ "" }else{ Md.header2("Constructors:") + representation.constructors.map(ls => htmlPreCode(representation.name + ls.paramLists.map(formatParamList(_, representation.path)).mkString(""), "scala") + "\n" + formatComments(ls.comments)).mkString("") + "\n" } } def formatMembers(): String = { val nonPrivateMembers = representation.members.filter(!_.isPrivate) val typeMembers = nonPrivateMembers.flatMap { case r: TypeRepresentation => Some(r) case _ => None } val objectMembers = nonPrivateMembers.flatMap { case r: ClassRepresentation if r.isObject => Some(r) case _ => None } val classMembers = nonPrivateMembers.flatMap { case r: ClassRepresentation if !r.isObject && !r.isTrait => Some(r) case _ => None } val traitMembers = nonPrivateMembers.flatMap { case r: ClassRepresentation if r.isTrait => Some(r) case _ => None } val defMembers = nonPrivateMembers.flatMap { case r: DefRepresentation => Some(r) case _ => None } val valMembers = nonPrivateMembers.flatMap { case r: ValRepresentation => Some(r) case _ => None } val abstractTypeMembers = typeMembers.filter(_.isAbstract).map(x => Md.header3(x.name) + formatRepresentationToMarkdown(x, declarationPath)).mkString("") + objectMembers.filter(_.isAbstract).map{x => traverseRepresentation(x) Md.header3(x.name.stripSuffix("$")) + formatSimplifiedClassRepresentation(x, declarationPath) }.mkString("") + classMembers.filter(_.isAbstract).map{x => traverseRepresentation(x) Md.header3(x.name) + formatSimplifiedClassRepresentation(x, declarationPath) }.mkString("") + traitMembers.filter(_.isAbstract).map{x => traverseRepresentation(x) Md.header3(x.name) + formatSimplifiedClassRepresentation(x, declarationPath) }.mkString("") val concreteTypeMembers = typeMembers.filter(!_.isAbstract).map(x => Md.header3(x.name) + formatRepresentationToMarkdown(x, declarationPath)).mkString("") + objectMembers.filter(!_.isAbstract).map{x => traverseRepresentation(x) Md.header3(x.name.stripSuffix("$")) + formatSimplifiedClassRepresentation(x, declarationPath) }.mkString("") + classMembers.filter(!_.isAbstract).map{x => traverseRepresentation(x) Md.header3(x.name) + formatSimplifiedClassRepresentation(x, declarationPath) }.mkString("") + traitMembers.filter(!_.isAbstract).map{x => traverseRepresentation(x) Md.header3(x.name) + formatSimplifiedClassRepresentation(x, declarationPath) }.mkString("") val abstractValueMembers = defMembers.filter(_.isAbstract).map(x => Md.header3(x.name) + formatRepresentationToMarkdown(x, declarationPath)).mkString("") + valMembers.filter(_.isAbstract).map(x => Md.header3(x.name) + formatRepresentationToMarkdown(x, declarationPath)).mkString("") val concreteValueMembers = defMembers.filter(!_.isAbstract).map(x => Md.header3(x.name) + formatRepresentationToMarkdown(x, declarationPath)).mkString("") + valMembers.filter(!_.isAbstract).map(x => Md.header3(x.name) + formatRepresentationToMarkdown(x, declarationPath)).mkString("") (if(abstractTypeMembers.nonEmpty){ Md.header2("Abstract Type Members:") + abstractTypeMembers }else{ "" }) + (if(concreteTypeMembers.nonEmpty){ Md.header2("Concrete Type Members:") + concreteTypeMembers }else{ "" }) + (if(abstractValueMembers.nonEmpty){ Md.header2("Abstract Value Members:") + abstractValueMembers }else{ "" }) + (if(concreteValueMembers.nonEmpty){ Md.header2("Concrete Value Members:") + concreteValueMembers }else{ "" }) } representation.path.mkString(".") + "\n" + Md.header1(representation.kind + " " + (if(representation.isObject) representation.name.stripSuffix("$") else representation.name)) + "\n" + formatCompanion() + formatSignature() + formatComments(representation.comments) + formatClassAnnotations() + formatKnownSubclasses() + formatConstructors() + formatMembers() } private def formatDefRepresentation(representation: DefRepresentation, declarationPath: List[String]): String = { htmlPreCode( formatAnnotations(representation.annotations, declarationPath) + formatModifiers(representation.modifiers, representation.privateWithin, representation.protectedWithin, representation.annotations, declarationPath) + "def " + representation.name + (if(representation.typeParams.nonEmpty) representation.typeParams.mkString("[", ", ", "]") else "") + representation.paramLists.map(formatParamList(_, declarationPath)).mkString("") + ": " + formatReferences(representation.returnValue, declarationPath) , "scala") + "\n" + { val com = formatComments(representation.comments) if(com == "") "\n" else com } } private def formatValRepresentation(representation: ValRepresentation, declarationPath: List[String]): String = { htmlPreCode( formatAnnotations(representation.annotations, declarationPath) + formatModifiers(representation.modifiers, representation.privateWithin, representation.protectedWithin, representation.annotations, declarationPath) + (if(representation.isVar) "var " else "val ") + representation.name + ": " + formatReferences(representation.returnValue, declarationPath) , "scala") + "\n" + formatComments(representation.comments) + "\n" } private def formatTypeRepresentation(representation: TypeRepresentation, declarationPath: List[String]): String = { htmlPreCode( formatAnnotations(representation.annotations, declarationPath) + formatModifiers(representation.modifiers, representation.privateWithin, representation.protectedWithin, representation.annotations, declarationPath) + "type " + representation.name + (if(representation.isAbstract) "" else ": " + formatReferences(representation.alias.get, declarationPath)) , "scala") + "\n" + formatComments(representation.comments) + "\n" } def formatRepresentationToMarkdown(representation: Representation, declarationPath: List[String], useSimplifiedFormat: Boolean = false): String = representation match { case r: EmulatedPackageRepresentation => if(useSimplifiedFormat){ htmlPreCode("package " + makeLink(r.name, (r.path :+ r.name).mkString("/", "/", ""), true, declarationPath), "scala") //Package file are at one level below the current package }else{ Md.header1("Package " + r.name) + Md.header2("Members:") + r.members.foldLeft("")((acc, m) => acc + formatRepresentationToMarkdown(m, declarationPath, true)) } case r : PackageRepresentation => htmlPreCode("package " + makeLink(r.name, (r.path :+ r.name).mkString("/", "/", ""), true, declarationPath), "scala") //Package file are at one level below the current package case r: ImportRepresentation => "" case r: ClassRepresentation => if(useSimplifiedFormat){ formatSimplifiedClassRepresentation(r, declarationPath) }else{ formatClassRepresentation(r, declarationPath) } case r: DefRepresentation => formatDefRepresentation(r, declarationPath) case r: ValRepresentation => formatValRepresentation(r, declarationPath) case r: TypeRepresentation => formatTypeRepresentation(r, declarationPath) } val folderPrefix = "tastydoc/documentation/" /** Traversing a Representation recursively producing markdown files along the way. In principle, this should be the only method called from outside of the class. */ def traverseRepresentation(representation: Representation): Unit = representation match { case r: EmulatedPackageRepresentation => r.members.foreach(traverseRepresentation) val file = new File("./" + folderPrefix + (r.path :+ r.name).mkString("/", "/", "/") + r.name + ".md") file.getParentFile.mkdirs val pw = new PrintWriter(file) pw.write(formatRepresentationToMarkdown(r, (r.path :+ r.name))) pw.close case r: PackageRepresentation => r.members.foreach(traverseRepresentation) case r: ClassRepresentation => val file = new File("./" + folderPrefix + r.path.mkString("", "/", "/") + r.name + ".md") file.getParentFile.mkdirs val pw = new PrintWriter(file) pw.write(formatRepresentationToMarkdown(r, r.path)) pw.close case _ => } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy