@@ -22,6 +22,8 @@ import org.pegdown.Printer
2222import org .pegdown .ast .DirectiveNode .Source
2323import org .pegdown .ast .{DirectiveNode , Visitor }
2424
25+ import scala .util .matching .Regex
26+
2527class ApidocDirective (allClassesAndObjects : IndexedSeq [String ], ctx : Writer .Context ) extends InlineDirective (" apidoc" ) {
2628 final val JavadocProperty = raw """ javadoc\.(.*)\.base_url """ .r
2729 final val JavadocBaseUrls = ctx.properties.collect {
@@ -33,16 +35,21 @@ class ApidocDirective(allClassesAndObjects: IndexedSeq[String], ctx: Writer.Cont
3335 private case class Query (label : Option [String ], pattern : String , generics : String , linkToObject : Boolean ) {
3436 def scalaLabel (matched : String ): String =
3537 label match {
36- case None => matched.split('.' ).last + generics
38+ case None => matched.split('.' ).last.replace( " $ " , " . " ) + generics
3739 case Some (la) => la + generics
3840 }
3941
42+ def scalaFqcn (matched : String ): String =
43+ matched.replace(" $" , " ." )
44+
4045 def javaLabel (matched : String ): String =
4146 scalaLabel(matched)
4247 .replaceAll(" \\ [" , " <" )
4348 .replaceAll(" \\ ]" , " >" )
4449 .replaceAll(" _" , " ?" )
4550
51+ def javaFqcn (matched : String ): String = scalaFqcn(matched)
52+
4653 override def toString =
4754 if (linkToObject) pattern + " $" + generics
4855 else pattern + generics
@@ -77,27 +84,50 @@ class ApidocDirective(allClassesAndObjects: IndexedSeq[String], ctx: Writer.Cont
7784 case s : Source .Direct => Query (node.label, s.value)
7885 }
7986 if (query.pattern.contains('.' )) {
80- if (allClasses.contains(query.pattern)) {
87+ val classNameWithDollarForInnerClasses = query.pattern.replaceAll(" (\\ b[A-Z].+)\\ ." , " $1\\ $" )
88+ if (allClasses.contains(classNameWithDollarForInnerClasses)) {
8189 renderMatches(query, Seq (query.pattern), node, visitor, printer)
82- } else
83- allClasses.filter(_.contains(query.pattern )) match {
90+ } else {
91+ allClasses.filter(_.contains(classNameWithDollarForInnerClasses )) match {
8492 case Seq () =>
8593 // No matches? then try globbing
86- val regex = (query.pattern.replaceAll( " \\ . " , " \\\\ . " ).replaceAll( " \\ * " , " .* " ) + " $ " ).r
94+ val regex = convertToRegex(classNameWithDollarForInnerClasses)
8795 allClasses.filter(cls => regex.findFirstMatchIn(cls).isDefined) match {
8896 case Seq () =>
89- ctx.error(s " Class not found for @apidoc[ $query] " , node)
97+ ctx.error(s " Class not found for @apidoc[ $query] (pattern $regex ) " , node)
9098 case results =>
9199 renderMatches(query, results, node, visitor, printer)
92100 }
93101 case results =>
94102 renderMatches(query, results, node, visitor, printer)
95103 }
104+ }
96105 } else {
97106 renderMatches(query, allClasses.filter(_.endsWith('.' + query.pattern)), node, visitor, printer)
98107 }
99108 }
100109
110+ private def convertToRegex (classNameWithDollarForInnerClasses : String ): Regex =
111+ (classNameWithDollarForInnerClasses
112+ .replaceAll(" \\ ." , " \\\\ ." )
113+ .replaceAll(" \\ *" , " .*" )
114+ .replaceAll(" \\ $" , " \\\\\\ $" ) + " $" ).r
115+
116+ private def scaladocNode (
117+ group : String ,
118+ label : String ,
119+ fqcn : String ,
120+ anchor : String ,
121+ node : DirectiveNode
122+ ): DirectiveNode = syntheticNode(group, " scala" , label, fqcn, anchor, node)
123+
124+ private def javadocNode (
125+ label : String ,
126+ fqcn : String ,
127+ anchor : String ,
128+ node : DirectiveNode
129+ ): DirectiveNode = syntheticNode(" java" , " java" , label, fqcn, anchor, node)
130+
101131 private def syntheticNode (
102132 group : String ,
103133 doctype : String ,
@@ -147,28 +177,31 @@ class ApidocDirective(allClassesAndObjects: IndexedSeq[String], ctx: Writer.Cont
147177 )
148178 case 1 =>
149179 val pkg = matches(0 )
150- syntheticNode(" scala" , " scala" , query.scalaLabel(pkg), pkg + scalaClassSuffix, sAnchor, node).accept(visitor)
180+ scaladocNode(" scala" , query.scalaLabel(pkg), query.scalaFqcn(pkg) + scalaClassSuffix, sAnchor, node)
181+ .accept(visitor)
151182 if (hasJavadocUrl(pkg)) {
152- syntheticNode( " java " , " java " , query.javaLabel(pkg), pkg, jAnchor, node).accept(visitor)
183+ javadocNode( query.javaLabel(pkg), query.javaFqcn( pkg) , jAnchor, node).accept(visitor)
153184 } else
154- syntheticNode(" java" , " scala" , query.scalaLabel(pkg), pkg + scalaClassSuffix, jAnchor, node).accept(visitor)
185+ scaladocNode(" java" , query.javaLabel(pkg), query.scalaFqcn(pkg) + scalaClassSuffix, jAnchor, node)
186+ .accept(visitor)
155187 case 2 if matches.forall(_.contains(" adsl" )) =>
156188 matches.foreach(pkg => {
157189 if (! pkg.contains(" javadsl" ))
158- syntheticNode (" scala" , " scala " , query.scalaLabel(pkg), pkg + scalaClassSuffix, sAnchor, node)
190+ scaladocNode (" scala" , query.scalaLabel(pkg), query.scalaFqcn( pkg) + scalaClassSuffix, sAnchor, node)
159191 .accept(visitor)
160192 if (! pkg.contains(" scaladsl" )) {
161193 if (hasJavadocUrl(pkg))
162- syntheticNode( " java " , " java " , query.javaLabel(pkg), pkg, jAnchor, node).accept(visitor)
194+ javadocNode( query.javaLabel(pkg), query.javaFqcn( pkg) , jAnchor, node).accept(visitor)
163195 else
164- syntheticNode (" java" , " scala " , query.scalaLabel (pkg), pkg + scalaClassSuffix, jAnchor, node)
196+ scaladocNode (" java" , query.javaLabel (pkg), query.scalaFqcn( pkg) + scalaClassSuffix, jAnchor, node)
165197 .accept(visitor)
166198 }
167199 })
168200 case n =>
169201 ctx.error(
170202 s " $n matches found for $query, but not javadsl/scaladsl: ${matches.mkString(" , " )}. " +
171- s " You may want to use the fully qualified class name as @apidoc[fqcn] instead of @apidoc[ $query]. " ,
203+ s " You may want to use the fully qualified class name as @apidoc[fqcn] instead of @apidoc[ $query]. " +
204+ s " For examples see https://github.com/lightbend/sbt-paradox-apidoc#examples " ,
172205 node
173206 )
174207 }
0 commit comments