Skip to content

Commit 46049bb

Browse files
committed
Use new API of Tasy inspector
Fix regression in DottyPlugin and in rendering Inheritance Diagram
1 parent 0b4cbeb commit 46049bb

File tree

7 files changed

+58
-103
lines changed

7 files changed

+58
-103
lines changed

sbt-dotty/src/dotty/tools/sbtplugin/DottyPlugin.scala

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,9 @@ object DottyPlugin extends AutoPlugin {
1818
val isDotty = settingKey[Boolean]("Is this project compiled with Dotty?")
1919
val isDottyJS = settingKey[Boolean]("Is this project compiled with Dotty and Scala.js?")
2020

21+
val useScala3doc = settingKey[Boolean]("Use Scala3doc as the documentation tool")
22+
val scala3docOptions = settingKey[Seq[String]]("Options for Scala3doc")
23+
2124
// NOTE:
2225
// - this is a def to support `scalaVersion := dottyLatestNightlyBuild`
2326
// - if this was a taskKey, then you couldn't do `scalaVersion := dottyLatestNightlyBuild`
@@ -373,7 +376,10 @@ object DottyPlugin extends AutoPlugin {
373376
// We need to add doctool classes to the classpath so they can be called
374377
scalaInstance in doc := Def.taskDyn {
375378
if (isDotty.value)
376-
dottyScalaInstanceTask(scala3Artefact(scalaVersion.value, "doc"))
379+
if (useScala3doc.value)
380+
dottyScalaInstanceTask("scala3doc")
381+
else
382+
dottyScalaInstanceTask(scala3Artefact(scalaVersion.value, "doc"))
377383
else
378384
Def.valueStrict { (scalaInstance in doc).taskValue }
379385
}.value,

scala3doc/src/dotty/dokka/IO.java

Lines changed: 3 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -3,64 +3,26 @@
33
import java.io.*;
44
import java.nio.file.*;
55
import java.nio.file.attribute.BasicFileAttributes;
6-
import java.util.zip.*;
76

87
/** This code is mostly using public snippets and tries to mimic sbt-io api. */
98
public class IO {
109
public static void delete(File pathToBeDeleted) throws IOException {
11-
Files.walkFileTree(pathToBeDeleted.toPath(),
10+
Files.walkFileTree(pathToBeDeleted.toPath(),
1211
new SimpleFileVisitor<Path>() {
1312
@Override
1413
public FileVisitResult postVisitDirectory(
1514
Path dir, IOException exc) throws IOException {
1615
Files.delete(dir);
1716
return FileVisitResult.CONTINUE;
1817
}
19-
18+
2019
@Override
2120
public FileVisitResult visitFile(
22-
Path file, BasicFileAttributes attrs)
21+
Path file, BasicFileAttributes attrs)
2322
throws IOException {
2423
Files.delete(file);
2524
return FileVisitResult.CONTINUE;
2625
}
2726
});
2827
}
29-
30-
private static File newFile(File destinationDir, ZipEntry zipEntry) throws IOException {
31-
File destFile = new File(destinationDir, zipEntry.getName());
32-
33-
String destDirPath = destinationDir.getCanonicalPath();
34-
String destFilePath = destFile.getCanonicalPath();
35-
36-
if (!destFilePath.startsWith(destDirPath + File.separator)) {
37-
throw new IOException("Entry is outside of the target dir: " + zipEntry.getName());
38-
}
39-
40-
return destFile;
41-
}
42-
43-
public static File unzip(File fileZip, File destDir) throws IOException {
44-
byte[] buffer = new byte[1024];
45-
ZipInputStream zis = null;
46-
47-
try {
48-
zis= new ZipInputStream(new FileInputStream(fileZip));
49-
ZipEntry zipEntry = zis.getNextEntry();
50-
while (zipEntry != null) {
51-
File newFile = newFile(destDir, zipEntry);
52-
FileOutputStream fos = new FileOutputStream(newFile);
53-
int len;
54-
while ((len = zis.read(buffer)) > 0) {
55-
fos.write(buffer, 0, len);
56-
}
57-
fos.close();
58-
zipEntry = zis.getNextEntry();
59-
}
60-
zis.closeEntry();
61-
} finally {
62-
if (zis != null) zis.close();
63-
}
64-
return destDir;
65-
}
6628
}

scala3doc/src/dotty/dokka/Main.scala

Lines changed: 15 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ trait BaseDocConfiguration:
103103
val tastyFiles: List[String]
104104

105105
enum DocConfiguration extends BaseDocConfiguration:
106-
case Standalone(args: Args, tastyFiles: List[String])
106+
case Standalone(args: Args, tastyFiles: List[String], tastyJars: List[String])
107107
case Sbt(args: Args, tastyFiles: List[String], rootCtx: DottyContext)
108108

109109
/** Main class for the doctool.
@@ -124,33 +124,27 @@ object Main:
124124
new CmdLineParser(rawArgs).parseArgument(args:_*)
125125
val parsedArgs = rawArgs.toArgs
126126

127-
val (jars, dirs) = parsedArgs.tastyRoots.partition(_.isFile)
128-
val extracted = jars.filter(_.exists()).map { jarFile =>
129-
val tempFile = Files.createTempDirectory("jar-unzipped").toFile
130-
IO.unzip(jarFile, tempFile)
131-
tempFile
132-
}
127+
val (files, dirs) = parsedArgs.tastyRoots.partition(_.isFile)
128+
val (providedTastyFiles, jars) = files.toList.map(_.getAbsolutePath).partition(_.endsWith(".tasty"))
129+
jars.foreach(j => if(!j.endsWith(".jar")) sys.error(s"Provided file $j is not jar not tasty file") )
133130

134-
try
135-
def listTastyFiles(f: File): Seq[String] =
136-
val (files, dirs) = f.listFiles().partition(_.isFile)
137-
ArraySeq.unsafeWrapArray(
138-
files.filter(_.getName.endsWith(".tasty")).map(_.toString) ++ dirs.flatMap(listTastyFiles)
139-
)
140-
val tastyFiles = (dirs ++ extracted).flatMap(listTastyFiles).toList
141131

142-
val config = DocConfiguration.Standalone(parsedArgs, tastyFiles)
132+
def listTastyFiles(f: File): Seq[String] =
133+
val (files, dirs) = f.listFiles().partition(_.isFile)
134+
ArraySeq.unsafeWrapArray(
135+
files.filter(_.getName.endsWith(".tasty")).map(_.toString) ++ dirs.flatMap(listTastyFiles)
136+
)
137+
val tastyFiles = providedTastyFiles ++ dirs.flatMap(listTastyFiles)
143138

144-
if (parsedArgs.output.exists()) IO.delete(parsedArgs.output)
139+
val config = DocConfiguration.Standalone(parsedArgs, tastyFiles, jars)
145140

146-
// TODO #20 pass options, classpath etc.
147-
new DokkaGenerator(new DottyDokkaConfig(config), DokkaConsoleLogger.INSTANCE).generate()
141+
if (parsedArgs.output.exists()) IO.delete(parsedArgs.output)
148142

149-
println("Done")
143+
// TODO #20 pass options, classpath etc.
144+
new DokkaGenerator(new DottyDokkaConfig(config), DokkaConsoleLogger.INSTANCE).generate()
150145

146+
println("Done")
151147

152-
finally
153-
extracted.foreach(IO.delete)
154148
// Sometimes jvm is hanging, so we want to be sure that we force shout down the jvm
155149
sys.exit(0)
156150
catch

scala3doc/src/dotty/dokka/ScalaModuleCreator.scala

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package dotty.dokka
22

3-
import org.jetbrains.dokka.{ DokkaConfiguration$DokkaSourceSet => DokkaSourceSet }
3+
import org.jetbrains.dokka.{ DokkaConfiguration$DokkaSourceSet => DokkaSourceSet }
44
import com.virtuslab.dokka.site.JavaSourceToDocumentableTranslator
55
import com.virtuslab.dokka.site.SourceSetWrapper
66
import org.jetbrains.dokka.plugability.DokkaContext
@@ -16,15 +16,15 @@ import org.jetbrains.dokka.base.parsers.MarkdownParser
1616
import collection.JavaConverters._
1717

1818
object ScalaModuleProvider extends JavaSourceToDocumentableTranslator:
19-
override def process(rawSourceSet: DokkaSourceSet, cxt: DokkaContext) =
19+
override def process(rawSourceSet: DokkaSourceSet, cxt: DokkaContext) =
2020
val sourceSet = SourceSetWrapper(rawSourceSet)
2121
cxt.getConfiguration match
2222
case dottyConfig: DottyDokkaConfig =>
2323
val result = dottyConfig.docConfiguration match {
24-
case DocConfiguration.Standalone(args, tastyFiles) =>
24+
case DocConfiguration.Standalone(args, tastyFiles, jars) =>
2525
// TODO use it to resolve link logic
2626
val inspector = DokkaTastyInspector(sourceSet, new MarkdownParser(_ => null), dottyConfig)
27-
inspector.inspect(args.classpath, tastyFiles)
27+
inspector.inspectAllTastyFiles(tastyFiles, jars, args.classpath.split(java.io.File.pathSeparator).toList)
2828
inspector.result()
2929
case DocConfiguration.Sbt(args, tastyFiles, rootCtx) =>
3030
val inspector =

scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,13 @@ class SignatureRenderer(pageContext: ContentPage, sourceSetRestriciton: JSet[Dis
2727
case Some(link) => a(href := link, modifiers)(name)
2828
case None if modifiers.isEmpty => raw(name)
2929
case _ => span(modifiers)(name)
30-
30+
3131

3232
def renderElementWith(e: String | (String, DRI) | Link, modifiers: AppliedAttr*) = e match
3333
case (name, dri) => renderLink(name, dri, modifiers:_*)
3434
case name: String => raw(name)
3535
case Link(name, dri) => renderLink(name, dri, modifiers:_*)
36-
36+
3737

3838
def renderElement(e: String | (String, DRI) | Link) = renderElementWith(e)
3939

@@ -65,7 +65,7 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) {
6565
node match {
6666
case n: HtmlContentNode => withHtml(f, raw(n.body).toString)
6767
case n: HierarchyDiagramContentNode => buildDiagram(f, n.diagram, pageContext)
68-
case n: DocumentableList =>
68+
case n: DocumentableList =>
6969
val ss = if sourceSetRestriciton == null then Set.empty.asJava else sourceSetRestriciton
7070
withHtml(f, buildDocumentableList(n, pageContext, ss).toString())
7171
case n: DocumentableFilter => withHtml(f, buildDocumentableFilter.toString)
@@ -80,15 +80,15 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) {
8080
</svg>
8181
""")
8282

83-
8483

85-
private def buildDocumentableList(n: DocumentableList, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) =
86-
def render(n: ContentNode) = raw(buildWithKotlinx(n, pageContext, null))
84+
85+
private def buildDocumentableList(n: DocumentableList, pageContext: ContentPage, sourceSetRestriciton: JSet[DisplaySourceSet]) =
86+
def render(n: ContentNode) = raw(buildWithKotlinx(n, pageContext, null))
8787

8888
val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider)
8989
import renderer._
9090

91-
def buildDocumentable(element: DocumentableElement) =
91+
def buildDocumentable(element: DocumentableElement) =
9292
def topLevelAttr = Seq(cls := "documentableElement") ++ element.attributes.map{ case (n, v) => Attr(s"data-f-$n") := v }
9393
val kind = element.modifiers.takeRight(1)
9494
val otherModifiers = element.modifiers.dropRight(1)
@@ -108,12 +108,12 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) {
108108
div(cls := "documentableBrief")(element.brief.map(render)),
109109
)
110110
),
111-
112-
)
111+
112+
)
113113

114114
div(cls := "documentableList")(
115115
if(n.groupName.isEmpty) raw("") else h3(cls := "documentableHeader")(n.groupName.map(renderElement)),
116-
n.elements.flatMap {
116+
n.elements.flatMap {
117117
case element: DocumentableElement =>
118118
Seq(buildDocumentable(element))
119119
case group: DocumentableElementGroup =>
@@ -184,13 +184,11 @@ class ScalaHtmlRenderer(ctx: DokkaContext) extends SiteRenderer(ctx) {
184184
})
185185
}
186186

187-
188-
def buildDiagram(f: FlowContent, diagram: HierarchyDiagram, pageContext: ContentPage) =
187+
def buildDiagram(f: FlowContent, diagram: HierarchyDiagram, pageContext: ContentPage) =
189188
val renderer = SignatureRenderer(pageContext, sourceSets, getLocationProvider)
190189
withHtml(f, div( id := "inheritance-diagram")(
191190
svg(id := "graph"),
192-
script(`type` := "text/dot", id := "dot"),
193-
raw(DotDiagramBuilder.build(diagram, renderer))
191+
script(`type` := "text/dot", id := "dot")(raw(DotDiagramBuilder.build(diagram, renderer))),
194192
).toString()
195193
)
196194

scala3doc/test/dotty/dokka/DottyTestRunner.scala

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -42,51 +42,51 @@ abstract class DottyAbstractCoreTest extends AbstractCoreTest:
4242
def listPages(tastyDir: String): Seq[ContentPage] =
4343
var signatures: Seq[ContentPage] = Nil
4444
val tests = new AbstractCoreTest$TestBuilder()
45-
45+
4646

4747
def getAllContentPages(root: PageNode) : Seq[ContentPage] = root match
4848
case c: ContentPage => Seq(c) ++ c.getChildren.asScala.flatMap(getAllContentPages)
4949
case default => default.getChildren.asScala.toSeq.flatMap(getAllContentPages)
5050

51-
tests.setPagesTransformationStage { root =>
51+
tests.setPagesTransformationStage { root =>
5252
val res = root.getChildren.asScala.flatMap(getAllContentPages)
5353
signatures = res.toSeq
5454
kotlin.Unit.INSTANCE
5555
}
5656

57-
def listTastyFiles(f: File): Seq[File] =
57+
def listTastyFiles(f: File): Seq[File] =
5858
assertTrue(s"Tasty root dir does not exisits: $f", f.isDirectory())
5959
val (files, dirs) = f.listFiles().partition(_.isFile)
6060
files.toIndexedSeq.filter(_.getName.endsWith(".tasty")) ++ dirs.flatMap(listTastyFiles)
6161

6262
val tastyFiles = tastyDir.split(File.pathSeparatorChar).toList.flatMap(p => listTastyFiles(new File(p))).map(_.toString)
63-
64-
val config = new DottyDokkaConfig(DocConfiguration.Standalone(args, tastyFiles))
63+
64+
val config = new DottyDokkaConfig(DocConfiguration.Standalone(args, tastyFiles, Nil))
6565
DokkaTestGenerator(
6666
config,
6767
new TestLogger(DokkaConsoleLogger.INSTANCE),
6868
tests.build(),
6969
Nil.asJava
70-
).generate()
70+
).generate()
7171

7272
signatures
7373

74-
def signaturesFromDocumentation(tastyDir: String): Seq[String] =
74+
def signaturesFromDocumentation(tastyDir: String): Seq[String] =
7575
def flattenToText(node: ContentNode) : Seq[String] = node match
7676
case t: ContentText => Seq(t.getText)
77-
case c: ContentComposite =>
77+
case c: ContentComposite =>
7878
c.getChildren.asScala.flatMap(flattenToText).toSeq
79-
case l: DocumentableElement =>
79+
case l: DocumentableElement =>
8080
(l.annotations ++ Seq(" ") ++ l.modifiers ++ Seq(l.name) ++ l.signature).map {
8181
case s: String => s
8282
case (s: String, _) => s
8383
case Link(s: String, _) => s
84-
}
84+
}
8585
case _ => Seq()
86-
86+
8787
def all(p: ContentNode => Boolean)(n: ContentNode): Seq[ContentNode] =
8888
if p(n) then Seq(n) else n.getChildren.asScala.toSeq.flatMap(all(p))
89-
89+
9090

9191
val pages = listPages(tastyDir)
9292
val nodes = pages.flatMap(p => all(_.isInstanceOf[DocumentableElement])(p.getContent))
@@ -96,15 +96,15 @@ abstract class DottyAbstractCoreTest extends AbstractCoreTest:
9696
val ExpectedRegex = ".+//expected: (.+)".r
9797
val UnexpectedRegex = "(.+)//unexpected".r
9898

99-
// e.g. to remove '(0)' from object IAmACaseObject extends CaseImplementThis/*<-*/(0)/*->*/
99+
// e.g. to remove '(0)' from object IAmACaseObject extends CaseImplementThis/*<-*/(0)/*->*/
100100
val CommentRegexp = """\/\*<-\*\/[^\/]+\/\*->\*\/"""
101101

102102
extension (s: String) def doesntStartWithAnyOfThese(c: Char*) = c.forall(char => !s.startsWith(char.toString))
103103
val lines = s.getLines().map(_.trim).toList
104104
.filter(_.doesntStartWithAnyOfThese('=',':','{','}'))
105105
.filterNot(_.trim.isEmpty)
106106
.filterNot(_.startsWith("//"))
107-
107+
108108
val expectedSignatures = lines.flatMap {
109109
case UnexpectedRegex(_) => None
110110
case ExpectedRegex(signature) => Some(signature)
@@ -117,7 +117,7 @@ abstract class DottyAbstractCoreTest extends AbstractCoreTest:
117117
}
118118

119119
SignaturesFromSource(expectedSignatures, unexpectedSignatures)
120-
120+
121121
val _collector = new ErrorCollector();
122122
@Rule
123123
def collector = _collector

scala3doc/test/dotty/dokka/tasty/comments/MemberLookupTests.scala

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ class MemberLookupTests {
116116
cases.testAll()
117117
}
118118

119-
Inspector().inspect("", listOurClasses())
119+
Inspector().inspectTastyFiles(listOurClasses())
120120
}
121121

122122
def listOurClasses(): List[String] = {
@@ -128,12 +128,7 @@ class MemberLookupTests {
128128
def go(bld: ListBuffer[String])(file: File): Unit =
129129
file.listFiles.foreach { f =>
130130
if f.isFile() then
131-
if f.toString.endsWith(".tasty") then
132-
bld.append(f.toString
133-
.stripPrefix(classRoot.toString + "/")
134-
.stripSuffix(".tasty")
135-
.replaceAll("/", ".")
136-
)
131+
if f.toString.endsWith(".tasty") then bld.append(f.toString)
137132
else go(bld)(f)
138133
}
139134

0 commit comments

Comments
 (0)