From aafd1a18c4cda2f91eace183316b4631dbca8aac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Mon, 21 Dec 2020 15:18:49 +0100 Subject: [PATCH 1/8] PoC --- build.sbt | 1 + project/Build.scala | 15 ++++ .../resources/scala3doc-searchbar.css | 84 +++++++++++++++++++ scala3doc-js/src/Globals.scala | 10 +++ scala3doc-js/src/Main.scala | 7 ++ scala3doc-js/src/searchbar/PageEntry.scala | 27 ++++++ scala3doc-js/src/searchbar/Searchbar.scala | 7 ++ .../src/searchbar/SearchbarComponent.scala | 73 ++++++++++++++++ .../src/searchbar/SearchbarGlobals.scala | 10 +++ .../src/searchbar/engine/Matchers.scala | 0 .../src/searchbar/engine/QueryParser.scala | 0 .../searchbar/engine/SearchbarEngine.scala | 6 ++ 12 files changed, 240 insertions(+) create mode 100644 scala3doc-js/resources/scala3doc-searchbar.css create mode 100644 scala3doc-js/src/Globals.scala create mode 100644 scala3doc-js/src/Main.scala create mode 100644 scala3doc-js/src/searchbar/PageEntry.scala create mode 100644 scala3doc-js/src/searchbar/Searchbar.scala create mode 100644 scala3doc-js/src/searchbar/SearchbarComponent.scala create mode 100644 scala3doc-js/src/searchbar/SearchbarGlobals.scala create mode 100644 scala3doc-js/src/searchbar/engine/Matchers.scala create mode 100644 scala3doc-js/src/searchbar/engine/QueryParser.scala create mode 100644 scala3doc-js/src/searchbar/engine/SearchbarEngine.scala diff --git a/build.sbt b/build.sbt index 6a350adb4936..b592aeaded08 100644 --- a/build.sbt +++ b/build.sbt @@ -22,6 +22,7 @@ val `tasty-core-bootstrapped` = Build.`tasty-core-bootstrapped` val `tasty-core-scala2` = Build.`tasty-core-scala2` val scala3doc = Build.scala3doc val `scala3doc-testcases` = Build.`scala3doc-testcases` +val `scala3doc-js` = Build.`scala3doc-js` val `scala3-bench-run` = Build.`scala3-bench-run` val dist = Build.dist val `community-build` = Build.`community-build` diff --git a/project/Build.scala b/project/Build.scala index 19244bfb36ec..8e0fef6aa789 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -28,6 +28,8 @@ import sbtbuildinfo.BuildInfoPlugin.autoImport._ import scala.util.Properties.isJavaAtLeast +import org.portablescala.sbtplatformdeps.PlatformDepsPlugin.autoImport._ + object MyScalaJSPlugin extends AutoPlugin { import Build._ @@ -1228,6 +1230,8 @@ object Build { lazy val `scala3doc` = project.in(file("scala3doc")).asScala3doc lazy val `scala3doc-testcases` = project.in(file("scala3doc-testcases")).asScala3docTestcases + lazy val `scala3doc-js` = project.in(file("scala3doc-js")).asScala3docJs + // sbt plugin to use Dotty in your own build, see // https://github.com/lampepfl/scala3-example-project for usage. lazy val `sbt-dotty` = project.in(file("sbt-dotty")). @@ -1653,6 +1657,17 @@ object Build { def asScala3docTestcases: Project = project.dependsOn(`scala3-compiler-bootstrapped`).settings(commonBootstrappedSettings) + def asScala3docJs: Project = + project. + enablePlugins(MyScalaJSPlugin). + dependsOn(`scala3-library-bootstrappedJS`). + settings( + fork in Test := false, + scalaJSUseMainModuleInitializer := true, + libraryDependencies += ("org.scala-js" %%% "scalajs-dom" % "1.1.0").withDottyCompat(scalaVersion.value) + ) + + def asDist(implicit mode: Mode): Project = project. enablePlugins(PackPlugin). withCommonSettings. diff --git a/scala3doc-js/resources/scala3doc-searchbar.css b/scala3doc-js/resources/scala3doc-searchbar.css new file mode 100644 index 000000000000..31f3688d1f74 --- /dev/null +++ b/scala3doc-js/resources/scala3doc-searchbar.css @@ -0,0 +1,84 @@ +:root { + --inkuire-logo-size: 20px; +} + +#scala3doc-search { + margin-top: 2px; + cursor: pointer; + position: fixed; + top: 0; + right: 20px; + z-index: 5; +} + +#scala3doc-search::before { + content: ""; + display:block; + height: var(--inkuire-logo-size); + width: var(--inkuire-logo-size); +} + +#scala3doc-searchbar.hidden { + display: none; +} + +#scala3doc-searchbar { + position: absolute; + top: 50px; + right: 40px; + width: calc(100% - 360px); + box-shadow: 0 2px 16px 0 rgba(0, 42, 76, 0.15); + font-size: 13px; + font-family: system-ui, -apple-system, Segoe UI, Roboto, Noto Sans, Ubuntu, Cantarell, Helvetica Neue, Arial, sans-serif; +} + +#scala3doc-searchbar-input { + width: 100%; + min-height: 32px; + border: none; + border-bottom: 1px solid #bbb; + padding: 10px; +} + +#scala3doc-searchbar-input:focus { + outline: none; +} + +#scala3doc-searchbar-results { + background: white; + display: flex; + flex-direction: column; + max-height: 500px; + overflow: auto; +} + +.scala3doc-searchbar-result { + line-height: 32px; + padding-left: 10px; + padding-right: 10px; +} + +.scala3doc-searchbar-result:first-of-type { + margin-top: 10px; +} + +.scala3doc-searchbar-result:hover { + background-color: #d4edff; +} + +.scala3doc-searchbar-result a { + color: #1f2326; +} + +.scala3doc-searchbar-result .scala3doc-searchbar-location { + color: gray; +} + +#searchBar { + display: inline-flex; +} + +.pull-right { + float: right; + margin-left: auto +} diff --git a/scala3doc-js/src/Globals.scala b/scala3doc-js/src/Globals.scala new file mode 100644 index 000000000000..e85230dede37 --- /dev/null +++ b/scala3doc-js/src/Globals.scala @@ -0,0 +1,10 @@ +package dotty.dokka + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSGlobalScope + +@js.native +@JSGlobalScope +object Globals extends js.Object { + val pathToRoot: String = js.native +} \ No newline at end of file diff --git a/scala3doc-js/src/Main.scala b/scala3doc-js/src/Main.scala new file mode 100644 index 000000000000..0481c9ef6b61 --- /dev/null +++ b/scala3doc-js/src/Main.scala @@ -0,0 +1,7 @@ +package dotty.dokka + +object Main extends App { + def initializeSearchbar(): Unit = Searchbar() + + initializeSearchbar() +} \ No newline at end of file diff --git a/scala3doc-js/src/searchbar/PageEntry.scala b/scala3doc-js/src/searchbar/PageEntry.scala new file mode 100644 index 000000000000..a23233fa9b59 --- /dev/null +++ b/scala3doc-js/src/searchbar/PageEntry.scala @@ -0,0 +1,27 @@ +package dotty.dokka + +import scala.scalajs.js + +@js.native +trait PageEntryJS extends js.Object { + val name: String = js.native + val description: String = js.native + val location: String = js.native + val searchKeys: js.Array[String] = js.native +} + +case class PageEntry( + name: String, + description: String, + location: String, + searchKeys: Array[String] +) + +object PageEntry { + def apply(jsObj: PageEntryJS): PageEntry = PageEntry( + jsObj.name, + jsObj.description, + jsObj.location, + jsObj.searchKeys.toArray + ) +} diff --git a/scala3doc-js/src/searchbar/Searchbar.scala b/scala3doc-js/src/searchbar/Searchbar.scala new file mode 100644 index 000000000000..f013bcd382eb --- /dev/null +++ b/scala3doc-js/src/searchbar/Searchbar.scala @@ -0,0 +1,7 @@ +package dotty.dokka + +class Searchbar { + val pages = SearchbarGlobals.pages.toList.map(PageEntry.apply) + val engine = SearchbarEngine(pages) + val component = SearchbarComponent(engine.query) +} diff --git a/scala3doc-js/src/searchbar/SearchbarComponent.scala b/scala3doc-js/src/searchbar/SearchbarComponent.scala new file mode 100644 index 000000000000..b2651e13a597 --- /dev/null +++ b/scala3doc-js/src/searchbar/SearchbarComponent.scala @@ -0,0 +1,73 @@ +package dotty.dokka + +import org.scalajs.dom._ +import org.scalajs.dom.html.Input + +class SearchbarComponent(val callback: (String) => List[PageEntry]) { + extension (p: PageEntry) + def toHTML = { + val wrapper = document.createElement("div") + wrapper.classList.add("scala3doc-searchbar-result") + wrapper.classList.add("monospace") + + val resultA = document.createElement("a").asInstanceOf[html.Anchor] + resultA.href = Globals.pathToRoot + p.location + resultA.text = s"${p.name}" + + val location = document.createElement("span") + location.classList.add("pull-right") + location.classList.add("scala3doc-searchbar-location") + location.textContent = p.description + + wrapper.appendChild(resultA) + wrapper.appendChild(location) + wrapper + } + + def handleNewQuery(query: String) = { + val result = callback(query).map(_.toHTML) + while (resultsDiv.hasChildNodes()) resultsDiv.removeChild(resultsDiv.lastChild) + result.foreach(resultsDiv.appendChild) + } + + private val logoClick: html.Span = { + val element = document.createElement("span").asInstanceOf[html.Span] + element.id = "scala3doc-search" + element.onclick = (event: Event) => + if (rootDiv.className.contains("hidden")) + rootDiv.className = rootShowClasses + else rootDiv.className = rootHiddenClasses + document.getElementById("searchBar").appendChild(element) + element + } + + private val input: html.Input = { + val element = document.createElement("input").asInstanceOf[html.Input] + element.id = "scala3doc-searchbar-input" + element.addEventListener("keyup", (e) => handleNewQuery(e.target.asInstanceOf[html.Input].value)) + element + } + + private val resultsDiv: html.Div = { + val element = document.createElement("div").asInstanceOf[html.Div] + element.id = "scala3doc-searchbar-results" + element + } + + private val rootHiddenClasses = "hidden" + private val rootShowClasses = "" + private val rootDiv: html.Div = { + val element = document.createElement("div").asInstanceOf[html.Div] + element.addEventListener("click", (e: Event) => e.stopPropagation()) + logoClick.addEventListener("click", (e: Event) => e.stopPropagation()) + document.body.addEventListener("click", (e: Event) => element.className = rootHiddenClasses) + element.className = rootHiddenClasses + element.id = "scala3doc-searchbar" + element.appendChild(input) + element.appendChild(resultsDiv) + document.body.appendChild(element) + element + } + + handleNewQuery("") +} \ No newline at end of file diff --git a/scala3doc-js/src/searchbar/SearchbarGlobals.scala b/scala3doc-js/src/searchbar/SearchbarGlobals.scala new file mode 100644 index 000000000000..3daa50cba493 --- /dev/null +++ b/scala3doc-js/src/searchbar/SearchbarGlobals.scala @@ -0,0 +1,10 @@ +package dotty.dokka + +import scala.scalajs.js +import scala.scalajs.js.annotation.JSGlobalScope + +@js.native +@JSGlobalScope +object SearchbarGlobals extends js.Object { + val pages: js.Array[PageEntryJS] = js.native +} \ No newline at end of file diff --git a/scala3doc-js/src/searchbar/engine/Matchers.scala b/scala3doc-js/src/searchbar/engine/Matchers.scala new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scala3doc-js/src/searchbar/engine/QueryParser.scala b/scala3doc-js/src/searchbar/engine/QueryParser.scala new file mode 100644 index 000000000000..e69de29bb2d1 diff --git a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala new file mode 100644 index 000000000000..7446947bf5d4 --- /dev/null +++ b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala @@ -0,0 +1,6 @@ +package dotty.dokka + +class SearchbarEngine(pages: List[PageEntry]) { + //TODO: Query should be parsed by QueryParser to list of filtering strategies called Matchers + def query(query: String): List[PageEntry] = pages +} \ No newline at end of file From 113e9343ae020e105edd26e8d946f76d51d8f21b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Tue, 22 Dec 2020 14:02:13 +0100 Subject: [PATCH 2/8] Add basic matchers and query parser --- scala3doc-js/src/searchbar/Searchbar.scala | 3 ++- .../src/searchbar/SearchbarComponent.scala | 2 +- .../src/searchbar/engine/Matchers.scala | 10 +++++++ .../src/searchbar/engine/QueryParser.scala | 27 +++++++++++++++++++ .../searchbar/engine/SearchbarEngine.scala | 7 +++-- 5 files changed, 45 insertions(+), 4 deletions(-) diff --git a/scala3doc-js/src/searchbar/Searchbar.scala b/scala3doc-js/src/searchbar/Searchbar.scala index f013bcd382eb..65347a9bd116 100644 --- a/scala3doc-js/src/searchbar/Searchbar.scala +++ b/scala3doc-js/src/searchbar/Searchbar.scala @@ -3,5 +3,6 @@ package dotty.dokka class Searchbar { val pages = SearchbarGlobals.pages.toList.map(PageEntry.apply) val engine = SearchbarEngine(pages) - val component = SearchbarComponent(engine.query) + val parser = QueryParser() + val component = SearchbarComponent(q => engine.query(parser.parse(q))) } diff --git a/scala3doc-js/src/searchbar/SearchbarComponent.scala b/scala3doc-js/src/searchbar/SearchbarComponent.scala index b2651e13a597..1cc6d1e384ef 100644 --- a/scala3doc-js/src/searchbar/SearchbarComponent.scala +++ b/scala3doc-js/src/searchbar/SearchbarComponent.scala @@ -44,7 +44,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]) { private val input: html.Input = { val element = document.createElement("input").asInstanceOf[html.Input] element.id = "scala3doc-searchbar-input" - element.addEventListener("keyup", (e) => handleNewQuery(e.target.asInstanceOf[html.Input].value)) + element.addEventListener("input", (e) => handleNewQuery(e.target.asInstanceOf[html.Input].value)) element } diff --git a/scala3doc-js/src/searchbar/engine/Matchers.scala b/scala3doc-js/src/searchbar/engine/Matchers.scala index e69de29bb2d1..5180397c5111 100644 --- a/scala3doc-js/src/searchbar/engine/Matchers.scala +++ b/scala3doc-js/src/searchbar/engine/Matchers.scala @@ -0,0 +1,10 @@ +package dotty.dokka + +enum Matchers(func: (PageEntry) => Int) extends Function1[PageEntry, Int]: + export func.apply + case ByName(query: String) extends Matchers( (p) => { + val name = p.searchKeys.headOption.map(_.toLowerCase) + name.filter(_.contains(query)).map(_ => p.name.size - query.size).getOrElse(-1) + }) + case ByKind(kind: String) extends Matchers((p) => p.name.split(" ").headOption.filter(_ == kind).fold(-1)(_ => 1)) + diff --git a/scala3doc-js/src/searchbar/engine/QueryParser.scala b/scala3doc-js/src/searchbar/engine/QueryParser.scala index e69de29bb2d1..3658f0a663b7 100644 --- a/scala3doc-js/src/searchbar/engine/QueryParser.scala +++ b/scala3doc-js/src/searchbar/engine/QueryParser.scala @@ -0,0 +1,27 @@ +package dotty.dokka + +import scala.util.matching.Regex._ +import scala.util.matching._ + +class QueryParser: + val kinds = Seq( + "class", + "trait", + "enum", + "object", + "def", + "val", + "var", + "package", + "given", + ) + val kindRegex = (kinds.mkString("(","|",")") + " (.+)").r + val restRegex = raw"(.+)".r + val escapedRegex = raw"`(.+)`".r + + def parse(query: String): List[Matchers] = query.toLowerCase match { + case escapedRegex(rest) => List(Matchers.ByName(rest)) + case kindRegex(kind, rest) => List(Matchers.ByKind(kind)) ++ parse(rest) + case restRegex(name) => List(Matchers.ByName(name)) + case _ => List() + } \ No newline at end of file diff --git a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala index 7446947bf5d4..9985ece8ea29 100644 --- a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala +++ b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala @@ -1,6 +1,9 @@ package dotty.dokka +import math.Ordering.Implicits.seqOrdering + class SearchbarEngine(pages: List[PageEntry]) { - //TODO: Query should be parsed by QueryParser to list of filtering strategies called Matchers - def query(query: String): List[PageEntry] = pages + def query(query: List[Matchers]): List[PageEntry] = { + pages.map(p => p -> query.map(_(p))).filterNot(_(1).exists(_ < 0)).sortBy(_(1)).map(_(0)) + } } \ No newline at end of file From 7f6e7f647e882e58456a216f0f976dddd4fd7ffa Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Mon, 28 Dec 2020 11:48:10 +0100 Subject: [PATCH 3/8] Add members which don't have page to search bar --- .../dotty/dokka/translators/ScalaSignatureProvider.scala | 2 +- .../dotty/renderers/ScalaSearchbarDataInstaller.scala | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala b/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala index ec70415ea6c5..fc4865097f2c 100644 --- a/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala +++ b/scala3doc/src/dotty/dokka/translators/ScalaSignatureProvider.scala @@ -48,7 +48,7 @@ object ScalaSignatureProvider: case tpe: Kind.Type => typeSignature(tpe, documentable, builder) case Kind.Package => - builder.text("package").text(" ").name(documentable.name, documentable.dri) + builder.text("package ").name(documentable.name, documentable.dri) case Kind.RootPackage => builder case Kind.Unknown => diff --git a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala index 9f654c00f053..4949aa07a18c 100644 --- a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala +++ b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala @@ -29,7 +29,14 @@ class ScalaSearchbarDataInstaller(val ctx: DokkaContext) extends SearchbarDataIn override def processPage(page: ContentPage, link: String) = Option(page.getDocumentable) match { - case Some(member) => processMember(member, link) + case Some(member) => { + // All members that don't have their own page + val all = member + .membersBy(m => m.kind != dotty.dokka.model.api.Kind.Package && !m.kind.isInstanceOf[Classlike]) + .filter(m => m.origin == Origin.RegularlyDefined && m.inheritedFrom.isEmpty) + all.foreach(processMember(_, link)) + processMember(member, link) + } case None => page match { case p: StaticPageNode => processStaticSite(p, link) case _ => () From 20b4115772ca1fd2e1f0094039884546b0dd54f5 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Mon, 4 Jan 2021 10:45:22 +0100 Subject: [PATCH 4/8] Automatically publish Scala3doc with Scala.js files --- project/Build.scala | 13 ++++++ .../resources/scala3doc-searchbar.css | 36 ++++++++++++--- scala3doc-js/src/Main.scala | 2 +- .../src/searchbar/SearchbarComponent.scala | 46 +++++++++++-------- .../resources/dotty_res/styles/search-bar.css | 28 +---------- .../ScalaEmbeddedResourceApppender.scala | 4 +- .../dotty/renderers/ScalaHtmlRenderer.scala | 5 +- 7 files changed, 77 insertions(+), 57 deletions(-) diff --git a/project/Build.scala b/project/Build.scala index 8e0fef6aa789..819ea1b0dcbb 100644 --- a/project/Build.scala +++ b/project/Build.scala @@ -1644,6 +1644,19 @@ object Build { ), Compile / buildInfoKeys := Seq[BuildInfoKey](version), Compile / buildInfoPackage := "dotty.dokka", + Compile / resourceGenerators += Def.task { + val jsDestinationFile = (Compile / resourceManaged).value / "dotty_res" / "scripts" / "searchbar.js" + sbt.IO.copyFile((fullOptJS in Compile in `scala3doc-js`).value.data, jsDestinationFile) + Seq(jsDestinationFile) + }.taskValue, + Compile / resourceGenerators += Def.task { + val cssDesitnationFile = (Compile / resourceManaged).value / "dotty_res" / "styles" / "scala3doc-searchbar.css" + val cssSourceFile = (resourceDirectory in Compile in `scala3doc-js`).value / "scala3doc-searchbar.css" + FileFunction.cached(streams.value.cacheDirectory / "css-cache") { (in: Set[File]) => + in.headOption.map(sbt.IO.copyFile(_, cssDesitnationFile)) + Set(cssDesitnationFile) + }.apply(Set(cssSourceFile)).toSeq + }.taskValue, testDocumentationRoot := (baseDirectory.value / "test-documentations").getAbsolutePath, buildInfoPackage in Test := "dotty.dokka.test", BuildInfoPlugin.buildInfoScopedSettings(Test), diff --git a/scala3doc-js/resources/scala3doc-searchbar.css b/scala3doc-js/resources/scala3doc-searchbar.css index 31f3688d1f74..4afe6c71af84 100644 --- a/scala3doc-js/resources/scala3doc-searchbar.css +++ b/scala3doc-js/resources/scala3doc-searchbar.css @@ -2,6 +2,35 @@ --inkuire-logo-size: 20px; } +/* button */ +.search span { + background: #ED3522; + fill: #fff; + cursor: pointer; + border: none; + padding: 9px; + border-radius: 24px; + box-shadow: 0 0 16px #F27264; +} +.search span:hover { + fill: #F27264; +} + +@media(max-width: 576px) { + .search span { + background: none; + fill: var(--icon-color); + cursor: pointer; + border: none; + padding: 0; + box-shadow: none; + margin-top: 2px; + } + .search span:hover { + fill: var(--link-hover-fg); + } +} + #scala3doc-search { margin-top: 2px; cursor: pointer; @@ -11,13 +40,6 @@ z-index: 5; } -#scala3doc-search::before { - content: ""; - display:block; - height: var(--inkuire-logo-size); - width: var(--inkuire-logo-size); -} - #scala3doc-searchbar.hidden { display: none; } diff --git a/scala3doc-js/src/Main.scala b/scala3doc-js/src/Main.scala index 0481c9ef6b61..9e677d0221d4 100644 --- a/scala3doc-js/src/Main.scala +++ b/scala3doc-js/src/Main.scala @@ -4,4 +4,4 @@ object Main extends App { def initializeSearchbar(): Unit = Searchbar() initializeSearchbar() -} \ No newline at end of file +} diff --git a/scala3doc-js/src/searchbar/SearchbarComponent.scala b/scala3doc-js/src/searchbar/SearchbarComponent.scala index 1cc6d1e384ef..07612df902d4 100644 --- a/scala3doc-js/src/searchbar/SearchbarComponent.scala +++ b/scala3doc-js/src/searchbar/SearchbarComponent.scala @@ -3,9 +3,9 @@ package dotty.dokka import org.scalajs.dom._ import org.scalajs.dom.html.Input -class SearchbarComponent(val callback: (String) => List[PageEntry]) { +class SearchbarComponent(val callback: (String) => List[PageEntry]): extension (p: PageEntry) - def toHTML = { + def toHTML = val wrapper = document.createElement("div") wrapper.classList.add("scala3doc-searchbar-result") wrapper.classList.add("monospace") @@ -22,41 +22,53 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]) { wrapper.appendChild(resultA) wrapper.appendChild(location) wrapper - } - def handleNewQuery(query: String) = { + def handleNewQuery(query: String) = val result = callback(query).map(_.toHTML) while (resultsDiv.hasChildNodes()) resultsDiv.removeChild(resultsDiv.lastChild) result.foreach(resultsDiv.appendChild) - } - private val logoClick: html.Span = { - val element = document.createElement("span").asInstanceOf[html.Span] - element.id = "scala3doc-search" - element.onclick = (event: Event) => + private val logoClick: html.Div = + val span = document.createElement("span").asInstanceOf[html.Span] + span.innerHTML = """""" + span.id = "scala3doc-search" + span.onclick = (event: Event) => if (rootDiv.className.contains("hidden")) rootDiv.className = rootShowClasses else rootDiv.className = rootHiddenClasses - document.getElementById("searchBar").appendChild(element) + + val element = createNestingDiv("search-content")( + createNestingDiv("search-conatiner")( + createNestingDiv("search")( + span + ) + ) + ) + document.getElementById("scala3doc-searchBar").appendChild(element) element - } - private val input: html.Input = { + + private val input: html.Input = val element = document.createElement("input").asInstanceOf[html.Input] element.id = "scala3doc-searchbar-input" element.addEventListener("input", (e) => handleNewQuery(e.target.asInstanceOf[html.Input].value)) element - } - private val resultsDiv: html.Div = { + private val resultsDiv: html.Div = val element = document.createElement("div").asInstanceOf[html.Div] element.id = "scala3doc-searchbar-results" element - } private val rootHiddenClasses = "hidden" private val rootShowClasses = "" - private val rootDiv: html.Div = { + + private def createNestingDiv(className: String)(innerElement: html.Element): html.Div = + val element = document.createElement("div").asInstanceOf[html.Div] + element.className = className + element.appendChild(innerElement) + element + + private val rootDiv: html.Div = val element = document.createElement("div").asInstanceOf[html.Div] element.addEventListener("click", (e: Event) => e.stopPropagation()) logoClick.addEventListener("click", (e: Event) => e.stopPropagation()) @@ -67,7 +79,5 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]) { element.appendChild(resultsDiv) document.body.appendChild(element) element - } handleNewQuery("") -} \ No newline at end of file diff --git a/scala3doc/resources/dotty_res/styles/search-bar.css b/scala3doc/resources/dotty_res/styles/search-bar.css index 820dcd8f21ad..fc5c7ed45c15 100644 --- a/scala3doc/resources/dotty_res/styles/search-bar.css +++ b/scala3doc/resources/dotty_res/styles/search-bar.css @@ -9,20 +9,6 @@ background: none; } -/* button */ -.search button { - background: #ED3522; - fill: #fff; - cursor: pointer; - border: none; - padding: 9px; - border-radius: 24px; - box-shadow: 0 0 16px #F27264; -} -.search button:hover { - fill: #F27264; -} - /* popup */ .popup-wrapper { box-shadow: 0 0 10px var(--border-light) !important; @@ -58,18 +44,6 @@ /* Portrait phones */ @media(max-width: 576px) { - .search button { - background: none; - fill: var(--icon-color); - cursor: pointer; - border: none; - padding: 0; - box-shadow: none; - margin-top: 2px; - } - .search button:hover { - fill: var(--link-hover-fg); - } .search-content { margin: 0 !important; top: 9px !important; @@ -88,4 +62,4 @@ min-width: 100%; width: auto !important; } -} \ No newline at end of file +} diff --git a/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala b/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala index dc6801135340..eb5463a8b675 100644 --- a/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala +++ b/scala3doc/src/dotty/dokka/preprocessors/ScalaEmbeddedResourceApppender.scala @@ -21,6 +21,7 @@ class ScalaEmbeddedResourceAppender extends PageTransformer { "styles/diagram.css", "styles/filter-bar.css", "styles/search-bar.css", + "styles/scala3doc-searchbar.css", "https://code.jquery.com/jquery-3.5.1.min.js", "https://d3js.org/d3.v6.min.js", "https://cdn.jsdelivr.net/npm/graphlib-dot@0.6.2/dist/graphlib-dot.min.js", @@ -36,7 +37,8 @@ class ScalaEmbeddedResourceAppender extends PageTransformer { "scripts/components/Input.js", "scripts/components/FilterGroup.js", "scripts/components/Filter.js", - "scripts/data.js" + "scripts/data.js", + "scripts/searchbar.js" )).asJava, page.getChildren ) diff --git a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala index 8112c04c3f88..15b4da14d5c3 100644 --- a/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala +++ b/scala3doc/src/dotty/renderers/ScalaHtmlRenderer.scala @@ -279,7 +279,7 @@ class ScalaHtmlRenderer(using ctx: DokkaContext) extends HtmlRenderer(ctx) { div (id := "leftToggler")( span(cls := "icon-toggler") ), - div(id := "searchBar"), + div(id := "scala3doc-searchBar"), main( raw(buildWithKotlinx(kotlinxContent)) ), @@ -297,8 +297,7 @@ class ScalaHtmlRenderer(using ctx: DokkaContext) extends HtmlRenderer(ctx) { ) ) ), - script(`type` := "text/javascript", src := resolveRoot(page, "scripts/pages.js")), - script(`type` := "text/javascript", src := resolveRoot(page, "scripts/main.js")) + script(`type` := "text/javascript", src := resolveRoot(page, "scripts/pages.js")) ) ).toString From 454a8e1d91e3a54cb5f8af596fe7939ced4140a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Thu, 7 Jan 2021 11:50:38 +0100 Subject: [PATCH 5/8] Minor bugfixes and adjustments of scala3doc searchbar --- scala3doc-js/resources/scala3doc-searchbar.css | 2 +- .../src/searchbar/SearchbarComponent.scala | 15 +++++++++------ scala3doc-js/src/searchbar/engine/Matchers.scala | 4 +++- .../src/searchbar/engine/QueryParser.scala | 6 +++--- .../src/searchbar/engine/SearchbarEngine.scala | 3 ++- 5 files changed, 18 insertions(+), 12 deletions(-) diff --git a/scala3doc-js/resources/scala3doc-searchbar.css b/scala3doc-js/resources/scala3doc-searchbar.css index 4afe6c71af84..cfa35d8ea497 100644 --- a/scala3doc-js/resources/scala3doc-searchbar.css +++ b/scala3doc-js/resources/scala3doc-searchbar.css @@ -32,7 +32,7 @@ } #scala3doc-search { - margin-top: 2px; + margin-top: 10px; cursor: pointer; position: fixed; top: 0; diff --git a/scala3doc-js/src/searchbar/SearchbarComponent.scala b/scala3doc-js/src/searchbar/SearchbarComponent.scala index 07612df902d4..fcd5c857f792 100644 --- a/scala3doc-js/src/searchbar/SearchbarComponent.scala +++ b/scala3doc-js/src/searchbar/SearchbarComponent.scala @@ -33,9 +33,10 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): span.innerHTML = """""" span.id = "scala3doc-search" span.onclick = (event: Event) => - if (rootDiv.className.contains("hidden")) - rootDiv.className = rootShowClasses - else rootDiv.className = rootHiddenClasses + if (document.body.contains(rootDiv)) { + document.body.removeChild(rootDiv) + } + else document.body.appendChild(rootDiv) val element = createNestingDiv("search-content")( createNestingDiv("search-conatiner")( @@ -72,12 +73,14 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): val element = document.createElement("div").asInstanceOf[html.Div] element.addEventListener("click", (e: Event) => e.stopPropagation()) logoClick.addEventListener("click", (e: Event) => e.stopPropagation()) - document.body.addEventListener("click", (e: Event) => element.className = rootHiddenClasses) - element.className = rootHiddenClasses + document.body.addEventListener("click", (e: Event) => + if (document.body.contains(element)) { + document.body.removeChild(element) + } + ) element.id = "scala3doc-searchbar" element.appendChild(input) element.appendChild(resultsDiv) - document.body.appendChild(element) element handleNewQuery("") diff --git a/scala3doc-js/src/searchbar/engine/Matchers.scala b/scala3doc-js/src/searchbar/engine/Matchers.scala index 5180397c5111..5fd61009bdc4 100644 --- a/scala3doc-js/src/searchbar/engine/Matchers.scala +++ b/scala3doc-js/src/searchbar/engine/Matchers.scala @@ -4,7 +4,9 @@ enum Matchers(func: (PageEntry) => Int) extends Function1[PageEntry, Int]: export func.apply case ByName(query: String) extends Matchers( (p) => { val name = p.searchKeys.headOption.map(_.toLowerCase) - name.filter(_.contains(query)).map(_ => p.name.size - query.size).getOrElse(-1) + //Edge case for empty query string + if query == "" then 1 + else name.filter(_.contains(query)).map(_ => p.name.size - query.size).getOrElse(-1) }) case ByKind(kind: String) extends Matchers((p) => p.name.split(" ").headOption.filter(_ == kind).fold(-1)(_ => 1)) diff --git a/scala3doc-js/src/searchbar/engine/QueryParser.scala b/scala3doc-js/src/searchbar/engine/QueryParser.scala index 3658f0a663b7..8d929ce472df 100644 --- a/scala3doc-js/src/searchbar/engine/QueryParser.scala +++ b/scala3doc-js/src/searchbar/engine/QueryParser.scala @@ -15,9 +15,9 @@ class QueryParser: "package", "given", ) - val kindRegex = (kinds.mkString("(","|",")") + " (.+)").r - val restRegex = raw"(.+)".r - val escapedRegex = raw"`(.+)`".r + val kindRegex = (kinds.mkString("(","|",")") + " (.*)").r + val restRegex = raw"(.*)".r + val escapedRegex = raw"`(.*)`".r def parse(query: String): List[Matchers] = query.toLowerCase match { case escapedRegex(rest) => List(Matchers.ByName(rest)) diff --git a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala index 9985ece8ea29..3d9352681e29 100644 --- a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala +++ b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala @@ -4,6 +4,7 @@ import math.Ordering.Implicits.seqOrdering class SearchbarEngine(pages: List[PageEntry]) { def query(query: List[Matchers]): List[PageEntry] = { - pages.map(p => p -> query.map(_(p))).filterNot(_(1).exists(_ < 0)).sortBy(_(1)).map(_(0)) + // Results list is limited to 100 entries for performance reasons + pages.map(p => p -> query.map(_(p))).filterNot(_(1).exists(_ < 0)).sortBy(_(1)).map(_(0)).take(100) } } \ No newline at end of file From de5491dc9d0e818876041bec32f3de205a7cabc4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Mon, 11 Jan 2021 15:28:08 +0100 Subject: [PATCH 6/8] Add search by acronym --- scala3doc-js/src/searchbar/PageEntry.scala | 8 +++++--- scala3doc-js/src/searchbar/SearchbarComponent.scala | 2 +- scala3doc-js/src/searchbar/engine/Matchers.scala | 13 ++++++++++--- scala3doc-js/src/searchbar/engine/QueryParser.scala | 5 +++-- .../renderers/ScalaSearchbarDataInstaller.scala | 5 ++++- 5 files changed, 23 insertions(+), 10 deletions(-) diff --git a/scala3doc-js/src/searchbar/PageEntry.scala b/scala3doc-js/src/searchbar/PageEntry.scala index a23233fa9b59..760b2d9236f4 100644 --- a/scala3doc-js/src/searchbar/PageEntry.scala +++ b/scala3doc-js/src/searchbar/PageEntry.scala @@ -11,10 +11,11 @@ trait PageEntryJS extends js.Object { } case class PageEntry( - name: String, + fullName: String, description: String, location: String, - searchKeys: Array[String] + shortName: String, + acronym: Option[String], ) object PageEntry { @@ -22,6 +23,7 @@ object PageEntry { jsObj.name, jsObj.description, jsObj.location, - jsObj.searchKeys.toArray + jsObj.searchKeys.head.toLowerCase, + Option.when(jsObj.searchKeys.size > 1)(jsObj.searchKeys.last) ) } diff --git a/scala3doc-js/src/searchbar/SearchbarComponent.scala b/scala3doc-js/src/searchbar/SearchbarComponent.scala index fcd5c857f792..9c17b67436d4 100644 --- a/scala3doc-js/src/searchbar/SearchbarComponent.scala +++ b/scala3doc-js/src/searchbar/SearchbarComponent.scala @@ -12,7 +12,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): val resultA = document.createElement("a").asInstanceOf[html.Anchor] resultA.href = Globals.pathToRoot + p.location - resultA.text = s"${p.name}" + resultA.text = s"${p.fullName}" val location = document.createElement("span") location.classList.add("pull-right") diff --git a/scala3doc-js/src/searchbar/engine/Matchers.scala b/scala3doc-js/src/searchbar/engine/Matchers.scala index 5fd61009bdc4..16a6c637c1fe 100644 --- a/scala3doc-js/src/searchbar/engine/Matchers.scala +++ b/scala3doc-js/src/searchbar/engine/Matchers.scala @@ -3,10 +3,17 @@ package dotty.dokka enum Matchers(func: (PageEntry) => Int) extends Function1[PageEntry, Int]: export func.apply case ByName(query: String) extends Matchers( (p) => { - val name = p.searchKeys.headOption.map(_.toLowerCase) + val nameOption = Option(p.shortName) + val acronym = p.acronym //Edge case for empty query string if query == "" then 1 - else name.filter(_.contains(query)).map(_ => p.name.size - query.size).getOrElse(-1) + else { + val results = List( + nameOption.filter(_.contains(query.toLowerCase)).fold(-1)(_.size - query.size), + acronym.filter(_.contains(query)).fold(-1)(_.size - query.size + 1) + ) + if results.forall(_ == -1) then -1 else results.filter(_ != -1).min + } }) - case ByKind(kind: String) extends Matchers((p) => p.name.split(" ").headOption.filter(_ == kind).fold(-1)(_ => 1)) + case ByKind(kind: String) extends Matchers((p) => p.fullName.split(" ").headOption.filter(_.equalsIgnoreCase(kind)).fold(-1)(_ => 1)) diff --git a/scala3doc-js/src/searchbar/engine/QueryParser.scala b/scala3doc-js/src/searchbar/engine/QueryParser.scala index 8d929ce472df..7d025c8e9866 100644 --- a/scala3doc-js/src/searchbar/engine/QueryParser.scala +++ b/scala3doc-js/src/searchbar/engine/QueryParser.scala @@ -14,12 +14,13 @@ class QueryParser: "var", "package", "given", + "type" ) - val kindRegex = (kinds.mkString("(","|",")") + " (.*)").r + val kindRegex = ("(?i)" + kinds.mkString("(","|",")") + " (.*)").r val restRegex = raw"(.*)".r val escapedRegex = raw"`(.*)`".r - def parse(query: String): List[Matchers] = query.toLowerCase match { + def parse(query: String): List[Matchers] = query match { case escapedRegex(rest) => List(Matchers.ByName(rest)) case kindRegex(kind, rest) => List(Matchers.ByKind(kind)) ++ parse(rest) case restRegex(name) => List(Matchers.ByName(name)) diff --git a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala index 4949aa07a18c..827ef1b75183 100644 --- a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala +++ b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala @@ -60,8 +60,11 @@ class ScalaSearchbarDataInstaller(val ctx: DokkaContext) extends SearchbarDataIn pages.addOne(p.getName + link, PageEntry(p.getName, p.getName, link, "")) } + private def createAcronym(s: String): Option[String] = + if s.head.isUpper then Some(s.filter(_.isUpper)) else None + override def generatePagesList(): String = { val mapper = jacksonObjectMapper() - val pagesList = pages.values.map(p => createSearchRecord(p.signature, p.pkg, p.link, List(p.name).asJava)).toList.asJava + val pagesList = pages.values.map(p => createSearchRecord(p.signature, p.pkg, p.link, (List(p.name) ++ createAcronym(p.name)).asJava)).toList.asJava mapper.writeValueAsString(pagesList) } From 625538df9a708ff4248453f089e497c695b9e36d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Thu, 14 Jan 2021 11:09:36 +0100 Subject: [PATCH 7/8] Add lazy loading of results. Improve UX. --- .../resources/scala3doc-searchbar.css | 4 --- .../src/searchbar/SearchbarComponent.scala | 27 ++++++++++++++----- .../searchbar/engine/SearchbarEngine.scala | 3 +-- .../ScalaSearchbarDataInstaller.scala | 2 +- 4 files changed, 23 insertions(+), 13 deletions(-) diff --git a/scala3doc-js/resources/scala3doc-searchbar.css b/scala3doc-js/resources/scala3doc-searchbar.css index cfa35d8ea497..accc9cae1a69 100644 --- a/scala3doc-js/resources/scala3doc-searchbar.css +++ b/scala3doc-js/resources/scala3doc-searchbar.css @@ -1,7 +1,3 @@ -:root { - --inkuire-logo-size: 20px; -} - /* button */ .search span { background: #ED3522; diff --git a/scala3doc-js/src/searchbar/SearchbarComponent.scala b/scala3doc-js/src/searchbar/SearchbarComponent.scala index 9c17b67436d4..504fe2dfd3f7 100644 --- a/scala3doc-js/src/searchbar/SearchbarComponent.scala +++ b/scala3doc-js/src/searchbar/SearchbarComponent.scala @@ -6,7 +6,7 @@ import org.scalajs.dom.html.Input class SearchbarComponent(val callback: (String) => List[PageEntry]): extension (p: PageEntry) def toHTML = - val wrapper = document.createElement("div") + val wrapper = document.createElement("div").asInstanceOf[html.Div] wrapper.classList.add("scala3doc-searchbar-result") wrapper.classList.add("monospace") @@ -25,8 +25,23 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): def handleNewQuery(query: String) = val result = callback(query).map(_.toHTML) + resultsDiv.scrollTop = 0 while (resultsDiv.hasChildNodes()) resultsDiv.removeChild(resultsDiv.lastChild) - result.foreach(resultsDiv.appendChild) + val fragment = document.createDocumentFragment() + result.take(100).foreach(fragment.appendChild) + resultsDiv.appendChild(fragment) + def loadMoreResults(result: List[raw.HTMLElement]): Unit = { + resultsDiv.onscroll = (event: Event) => { + if (resultsDiv.scrollHeight - resultsDiv.scrollTop == resultsDiv.clientHeight) + { + val fragment = document.createDocumentFragment() + result.take(100).foreach(fragment.appendChild) + resultsDiv.appendChild(fragment) + loadMoreResults(result.drop(100)) + } + } + } + loadMoreResults(result.drop(100)) private val logoClick: html.Div = val span = document.createElement("span").asInstanceOf[html.Span] @@ -39,7 +54,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): else document.body.appendChild(rootDiv) val element = createNestingDiv("search-content")( - createNestingDiv("search-conatiner")( + createNestingDiv("search-container")( createNestingDiv("search")( span ) @@ -71,9 +86,9 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): private val rootDiv: html.Div = val element = document.createElement("div").asInstanceOf[html.Div] - element.addEventListener("click", (e: Event) => e.stopPropagation()) - logoClick.addEventListener("click", (e: Event) => e.stopPropagation()) - document.body.addEventListener("click", (e: Event) => + element.addEventListener("mousedown", (e: Event) => e.stopPropagation()) + logoClick.addEventListener("mousedown", (e: Event) => e.stopPropagation()) + document.body.addEventListener("mousedown", (e: Event) => if (document.body.contains(element)) { document.body.removeChild(element) } diff --git a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala index 3d9352681e29..9985ece8ea29 100644 --- a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala +++ b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala @@ -4,7 +4,6 @@ import math.Ordering.Implicits.seqOrdering class SearchbarEngine(pages: List[PageEntry]) { def query(query: List[Matchers]): List[PageEntry] = { - // Results list is limited to 100 entries for performance reasons - pages.map(p => p -> query.map(_(p))).filterNot(_(1).exists(_ < 0)).sortBy(_(1)).map(_(0)).take(100) + pages.map(p => p -> query.map(_(p))).filterNot(_(1).exists(_ < 0)).sortBy(_(1)).map(_(0)) } } \ No newline at end of file diff --git a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala index 827ef1b75183..33b20137ff5f 100644 --- a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala +++ b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala @@ -61,7 +61,7 @@ class ScalaSearchbarDataInstaller(val ctx: DokkaContext) extends SearchbarDataIn } private def createAcronym(s: String): Option[String] = - if s.head.isUpper then Some(s.filter(_.isUpper)) else None + s.headOption.filter(_.isUpper).map(_ => s.filter(_.isUpper)) override def generatePagesList(): String = { val mapper = jacksonObjectMapper() From d0a89928cfcd4e993e7255fb39b53c7605275795 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Zyba=C5=82a?= Date: Fri, 15 Jan 2021 10:28:00 +0100 Subject: [PATCH 8/8] Change requests --- scala3doc-js/src/Main.scala | 4 +-- scala3doc-js/src/searchbar/PageEntry.scala | 7 ++-- .../src/searchbar/SearchbarComponent.scala | 13 +++---- .../src/searchbar/engine/Matchers.scala | 34 +++++++++++-------- .../searchbar/engine/SearchbarEngine.scala | 14 +++++++- .../ScalaSearchbarDataInstaller.scala | 5 +-- 6 files changed, 46 insertions(+), 31 deletions(-) diff --git a/scala3doc-js/src/Main.scala b/scala3doc-js/src/Main.scala index 9e677d0221d4..6c179a1890a0 100644 --- a/scala3doc-js/src/Main.scala +++ b/scala3doc-js/src/Main.scala @@ -1,7 +1,5 @@ package dotty.dokka object Main extends App { - def initializeSearchbar(): Unit = Searchbar() - - initializeSearchbar() + Searchbar() } diff --git a/scala3doc-js/src/searchbar/PageEntry.scala b/scala3doc-js/src/searchbar/PageEntry.scala index 760b2d9236f4..046cf428ebf9 100644 --- a/scala3doc-js/src/searchbar/PageEntry.scala +++ b/scala3doc-js/src/searchbar/PageEntry.scala @@ -15,15 +15,18 @@ case class PageEntry( description: String, location: String, shortName: String, - acronym: Option[String], + acronym: Option[String] ) object PageEntry { + private def createAcronym(s: String): Option[String] = + s.headOption.map(firstLetter => firstLetter.toString ++ s.tail.filter(_.isUpper)) + def apply(jsObj: PageEntryJS): PageEntry = PageEntry( jsObj.name, jsObj.description, jsObj.location, jsObj.searchKeys.head.toLowerCase, - Option.when(jsObj.searchKeys.size > 1)(jsObj.searchKeys.last) + createAcronym(jsObj.searchKeys.head) ) } diff --git a/scala3doc-js/src/searchbar/SearchbarComponent.scala b/scala3doc-js/src/searchbar/SearchbarComponent.scala index 504fe2dfd3f7..e247ddab862f 100644 --- a/scala3doc-js/src/searchbar/SearchbarComponent.scala +++ b/scala3doc-js/src/searchbar/SearchbarComponent.scala @@ -4,6 +4,7 @@ import org.scalajs.dom._ import org.scalajs.dom.html.Input class SearchbarComponent(val callback: (String) => List[PageEntry]): + val resultsChunkSize = 100 extension (p: PageEntry) def toHTML = val wrapper = document.createElement("div").asInstanceOf[html.Div] @@ -28,22 +29,22 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): resultsDiv.scrollTop = 0 while (resultsDiv.hasChildNodes()) resultsDiv.removeChild(resultsDiv.lastChild) val fragment = document.createDocumentFragment() - result.take(100).foreach(fragment.appendChild) + result.take(resultsChunkSize).foreach(fragment.appendChild) resultsDiv.appendChild(fragment) def loadMoreResults(result: List[raw.HTMLElement]): Unit = { resultsDiv.onscroll = (event: Event) => { if (resultsDiv.scrollHeight - resultsDiv.scrollTop == resultsDiv.clientHeight) { val fragment = document.createDocumentFragment() - result.take(100).foreach(fragment.appendChild) + result.take(resultsChunkSize).foreach(fragment.appendChild) resultsDiv.appendChild(fragment) - loadMoreResults(result.drop(100)) + loadMoreResults(result.drop(resultsChunkSize)) } } } - loadMoreResults(result.drop(100)) + loadMoreResults(result.drop(resultsChunkSize)) - private val logoClick: html.Div = + private val searchIcon: html.Div = val span = document.createElement("span").asInstanceOf[html.Span] span.innerHTML = """""" span.id = "scala3doc-search" @@ -87,7 +88,7 @@ class SearchbarComponent(val callback: (String) => List[PageEntry]): private val rootDiv: html.Div = val element = document.createElement("div").asInstanceOf[html.Div] element.addEventListener("mousedown", (e: Event) => e.stopPropagation()) - logoClick.addEventListener("mousedown", (e: Event) => e.stopPropagation()) + searchIcon.addEventListener("mousedown", (e: Event) => e.stopPropagation()) document.body.addEventListener("mousedown", (e: Event) => if (document.body.contains(element)) { document.body.removeChild(element) diff --git a/scala3doc-js/src/searchbar/engine/Matchers.scala b/scala3doc-js/src/searchbar/engine/Matchers.scala index 16a6c637c1fe..faa3dc041d86 100644 --- a/scala3doc-js/src/searchbar/engine/Matchers.scala +++ b/scala3doc-js/src/searchbar/engine/Matchers.scala @@ -1,19 +1,23 @@ package dotty.dokka -enum Matchers(func: (PageEntry) => Int) extends Function1[PageEntry, Int]: - export func.apply - case ByName(query: String) extends Matchers( (p) => { - val nameOption = Option(p.shortName) - val acronym = p.acronym - //Edge case for empty query string - if query == "" then 1 - else { - val results = List( - nameOption.filter(_.contains(query.toLowerCase)).fold(-1)(_.size - query.size), - acronym.filter(_.contains(query)).fold(-1)(_.size - query.size + 1) - ) - if results.forall(_ == -1) then -1 else results.filter(_ != -1).min +enum Matchers extends Function1[PageEntry, Int]: + case ByName(query: String) + case ByKind(kind: String) + + def apply(p: PageEntry): Int = this match { + case ByName(query) => { + val nameOption = Option(p.shortName) + val acronym = p.acronym + //Edge case for empty query string + if query == "" then 1 + else { + val results = List( + nameOption.filter(_.contains(query.toLowerCase)).fold(-1)(_.size - query.size), + acronym.filter(_.contains(query)).fold(-1)(_.size - query.size + 1) + ) + if results.forall(_ == -1) then -1 else results.filter(_ != -1).min + } } - }) - case ByKind(kind: String) extends Matchers((p) => p.fullName.split(" ").headOption.filter(_.equalsIgnoreCase(kind)).fold(-1)(_ => 1)) + case ByKind(kind) => p.fullName.split(" ").headOption.filter(_.equalsIgnoreCase(kind)).fold(-1)(_ => 1) + } diff --git a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala index 9985ece8ea29..d0398f233ae9 100644 --- a/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala +++ b/scala3doc-js/src/searchbar/engine/SearchbarEngine.scala @@ -4,6 +4,18 @@ import math.Ordering.Implicits.seqOrdering class SearchbarEngine(pages: List[PageEntry]) { def query(query: List[Matchers]): List[PageEntry] = { - pages.map(p => p -> query.map(_(p))).filterNot(_(1).exists(_ < 0)).sortBy(_(1)).map(_(0)) + pages + .map( page => + page -> query.map(matcher => matcher(page)) + ) + .filterNot { + case (page, matchResults) => matchResults.exists(_ < 0) + } + .sortBy { + case (page, matchResults) => matchResults + } + .map { + case (page, matchResults) => page + } } } \ No newline at end of file diff --git a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala index 33b20137ff5f..3d0b1103b0d7 100644 --- a/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala +++ b/scala3doc/src/dotty/renderers/ScalaSearchbarDataInstaller.scala @@ -60,11 +60,8 @@ class ScalaSearchbarDataInstaller(val ctx: DokkaContext) extends SearchbarDataIn pages.addOne(p.getName + link, PageEntry(p.getName, p.getName, link, "")) } - private def createAcronym(s: String): Option[String] = - s.headOption.filter(_.isUpper).map(_ => s.filter(_.isUpper)) - override def generatePagesList(): String = { val mapper = jacksonObjectMapper() - val pagesList = pages.values.map(p => createSearchRecord(p.signature, p.pkg, p.link, (List(p.name) ++ createAcronym(p.name)).asJava)).toList.asJava + val pagesList = pages.values.map(p => createSearchRecord(p.signature, p.pkg, p.link, (List(p.name)).asJava)).toList.asJava mapper.writeValueAsString(pagesList) }