@@ -6,7 +6,7 @@ import scala.util.matching.Regex
66
77import org .jetbrains .dokka .pages .{RootPageNode , PageNode , ContentPage , ContentText , ContentNode , ContentComposite }
88
9- import dotty .dokka .model .api .Link
9+ import dotty .dokka .model .api ._
1010
1111private enum Signature :
1212 case Expected (name : String , signature : String )
@@ -17,7 +17,8 @@ abstract class SignatureTest(
1717 testName : String ,
1818 signatureKinds : Seq [String ],
1919 sourceFiles : List [String ] = Nil ,
20- ignoreMissingSignatures : Boolean = false
20+ ignoreMissingSignatures : Boolean = false ,
21+ filterFunc : (Member ) => Boolean = _ => true
2122) extends ScaladocTest (testName):
2223 override def assertions = Assertion .AfterPagesTransformation { root =>
2324 val sources = sourceFiles match
@@ -63,63 +64,64 @@ abstract class SignatureTest(
6364
6465 } :: Nil
6566
67+ // e.g. to remove '(0)' from object IAmACaseObject extends CaseImplementThis/*<-*/(0)/*->*/
68+ private val commentRegex = raw " \/\*<-\*\/[^\/]+\/\*->\*\/ " .r
69+ private val whitespaceRegex = raw " \s+ " .r
70+ private val expectedRegex = raw " .+//expected: (.+) " .r
71+ private val unexpectedRegex = raw " (.+)//unexpected " .r
72+ private val identifierRegex = raw " ^\s*(`.*`|(?:\w+)(?:_[^\[\(\s]+)|\w+|[^\[\(\s]+) " .r
73+
74+ private def findMissingSingatures (expected : Seq [String ], actual : Seq [String ]): Set [String ] =
75+ expected.toSet &~ actual.toSet
76+
77+ extension (s : String ):
78+ private def startWithAnyOfThese (c : String * ) = c.exists(s.startsWith)
79+ private def compactWhitespaces = whitespaceRegex.replaceAllIn(s, " " )
80+
81+ private def findName (signature : String , kinds : Seq [String ]): Option [String ] =
82+ for
83+ kindMatch <- kinds.flatMap(k => s " \\ b $k\\ b " .r.findFirstMatchIn(signature)).headOption
84+ afterKind <- Option (kindMatch.after(0 )) // to filter out nulls
85+ nameMatch <- identifierRegex.findFirstMatchIn(afterKind)
86+ yield nameMatch.group(1 )
87+
88+ private def signaturesFromSources (source : Source , kinds : Seq [String ]): Seq [Signature ] =
89+ source.getLines.map(_.trim)
90+ .filterNot(_.isEmpty)
91+ .filterNot(_.startWithAnyOfThese(" =" ," :" ," {" ," }" , " //" ))
92+ .toSeq
93+ .flatMap {
94+ case unexpectedRegex(signature) => findName(signature, kinds).map(Unexpected (_))
95+ case expectedRegex(signature) => findName(signature, kinds).map(Expected (_, signature))
96+ case signature =>
97+ findName(signature, kinds).map(Expected (_, commentRegex.replaceAllIn(signature, " " ).compactWhitespaces))
98+ }
99+
100+ private def signaturesFromDocumentation (root : PageNode ): Seq [String ] =
101+ def flattenToText (node : ContentNode ) : Seq [String ] = node match
102+ case t : ContentText => Seq (t.getText)
103+ case c : ContentComposite =>
104+ c.getChildren.asScala.flatMap(flattenToText).toSeq
105+ case l : DocumentableElement =>
106+ (l.annotations ++ Seq (" " ) ++ l.modifiers ++ Seq (l.name) ++ l.signature).map {
107+ case s : String => s
108+ case Link (s : String , _) => s
109+ }
110+ case _ => Seq ()
111+
112+ def all (p : ContentNode => Boolean )(n : ContentNode ): Seq [ContentNode ] =
113+ if p(n) then Seq (n) else n.getChildren.asScala.toSeq.flatMap(all(p))
114+
115+ extension (page : PageNode ) def allPages : List [PageNode ] = page :: page.getChildren.asScala.toList.flatMap(_.allPages)
116+
117+ val nodes = root.allPages
118+ .collect { case p : ContentPage => p }
119+ .filter( p => Option (p.getDocumentable).map(filterFunc).getOrElse(true ))
120+ .flatMap(p => all(_.isInstanceOf [DocumentableElement ])(p.getContent))
121+ nodes.map(flattenToText(_).mkString.compactWhitespaces.trim)
122+
66123object SignatureTest {
67124 val classlikeKinds = Seq (" class" , " object" , " trait" , " enum" ) // TODO add docs for packages
68125 val members = Seq (" type" , " def" , " val" , " var" )
69126 val all = classlikeKinds ++ members
70127}
71-
72- // e.g. to remove '(0)' from object IAmACaseObject extends CaseImplementThis/*<-*/(0)/*->*/
73- private val commentRegex = raw " \/\*<-\*\/[^\/]+\/\*->\*\/ " .r
74- private val whitespaceRegex = raw " \s+ " .r
75- private val expectedRegex = raw " .+//expected: (.+) " .r
76- private val unexpectedRegex = raw " (.+)//unexpected " .r
77- private val identifierRegex = raw " ^\s*(`.*`|(?:\w+)(?:_[^\[\(\s]+)|\w+|[^\[\(\s]+) " .r
78-
79- private def findMissingSingatures (expected : Seq [String ], actual : Seq [String ]): Set [String ] =
80- expected.toSet &~ actual.toSet
81-
82- extension (s : String ):
83- private def startWithAnyOfThese (c : String * ) = c.exists(s.startsWith)
84- private def compactWhitespaces = whitespaceRegex.replaceAllIn(s, " " )
85-
86- private def findName (signature : String , kinds : Seq [String ]): Option [String ] =
87- for
88- kindMatch <- kinds.flatMap(k => s " \\ b $k\\ b " .r.findFirstMatchIn(signature)).headOption
89- afterKind <- Option (kindMatch.after(0 )) // to filter out nulls
90- nameMatch <- identifierRegex.findFirstMatchIn(afterKind)
91- yield nameMatch.group(1 )
92-
93- private def signaturesFromSources (source : Source , kinds : Seq [String ]): Seq [Signature ] =
94- source.getLines.map(_.trim)
95- .filterNot(_.isEmpty)
96- .filterNot(_.startWithAnyOfThese(" =" ," :" ," {" ," }" , " //" ))
97- .toSeq
98- .flatMap {
99- case unexpectedRegex(signature) => findName(signature, kinds).map(Unexpected (_))
100- case expectedRegex(signature) => findName(signature, kinds).map(Expected (_, signature))
101- case signature =>
102- findName(signature, kinds).map(Expected (_, commentRegex.replaceAllIn(signature, " " ).compactWhitespaces))
103- }
104-
105- private def signaturesFromDocumentation (root : PageNode ): Seq [String ] =
106- def flattenToText (node : ContentNode ) : Seq [String ] = node match
107- case t : ContentText => Seq (t.getText)
108- case c : ContentComposite =>
109- c.getChildren.asScala.flatMap(flattenToText).toSeq
110- case l : DocumentableElement =>
111- (l.annotations ++ Seq (" " ) ++ l.modifiers ++ Seq (l.name) ++ l.signature).map {
112- case s : String => s
113- case Link (s : String , _) => s
114- }
115- case _ => Seq ()
116-
117- def all (p : ContentNode => Boolean )(n : ContentNode ): Seq [ContentNode ] =
118- if p(n) then Seq (n) else n.getChildren.asScala.toSeq.flatMap(all(p))
119-
120- extension (page : PageNode ) def allPages : List [PageNode ] = page :: page.getChildren.asScala.toList.flatMap(_.allPages)
121-
122- val nodes = root.allPages
123- .collect { case p : ContentPage => p }
124- .flatMap(p => all(_.isInstanceOf [DocumentableElement ])(p.getContent))
125- nodes.map(flattenToText(_).mkString.compactWhitespaces.trim)
0 commit comments